Mario Kart 64
Loading...
Searching...
No Matches
miniaudio.h
Go to the documentation of this file.
1/*
2Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
3miniaudio - v0.11.22 - 2025-02-24
4
5David Reid - mackron@gmail.com
6
7Website: https://miniaud.io
8Documentation: https://miniaud.io/docs
9GitHub: https://github.com/mackron/miniaudio
10*/
11
12/*
131. Introduction
14===============
15To use miniaudio, include "miniaudio.h":
16
17 ```c
18 #include "miniaudio.h"
19 ```
20
21The implementation is contained in "miniaudio.c". Just compile this like any other source file. You
22can include miniaudio.c if you want to compile your project as a single translation unit:
23
24 ```c
25 #include "miniaudio.c"
26 ```
27
28miniaudio includes both low level and high level APIs. The low level API is good for those who want
29to do all of their mixing themselves and only require a light weight interface to the underlying
30audio device. The high level API is good for those who have complex mixing and effect requirements.
31
32In miniaudio, objects are transparent structures. Unlike many other libraries, there are no handles
33to opaque objects which means you need to allocate memory for objects yourself. In the examples
34presented in this documentation you will often see objects declared on the stack. You need to be
35careful when translating these examples to your own code so that you don't accidentally declare
36your objects on the stack and then cause them to become invalid once the function returns. In
37addition, you must ensure the memory address of your objects remain the same throughout their
38lifetime. You therefore cannot be making copies of your objects.
39
40A config/init pattern is used throughout the entire library. The idea is that you set up a config
41object and pass that into the initialization routine. The advantage to this system is that the
42config object can be initialized with logical defaults and new properties added to it without
43breaking the API. The config object can be allocated on the stack and does not need to be
44maintained after initialization of the corresponding object.
45
46
471.1. Low Level API
48------------------
49The low level API gives you access to the raw audio data of an audio device. It supports playback,
50capture, full-duplex and loopback (WASAPI only). You can enumerate over devices to determine which
51physical device(s) you want to connect to.
52
53The low level API uses the concept of a "device" as the abstraction for physical devices. The idea
54is that you choose a physical device to emit or capture audio from, and then move data to/from the
55device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a
56callback which you specify when initializing the device.
57
58When initializing the device you first need to configure it. The device configuration allows you to
59specify things like the format of the data delivered via the callback, the size of the internal
60buffer and the ID of the device you want to emit or capture audio from.
61
62Once you have the device configuration set up you can initialize the device. When initializing a
63device you need to allocate memory for the device object beforehand. This gives the application
64complete control over how the memory is allocated. In the example below we initialize a playback
65device on the stack, but you could allocate it on the heap if that suits your situation better.
66
67 ```c
68 void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
69 {
70 // In playback mode copy data to pOutput. In capture mode read data from pInput. In full-duplex mode, both
71 // pOutput and pInput will be valid and you can move data from pInput into pOutput. Never process more than
72 // frameCount frames.
73 }
74
75 int main()
76 {
77 ma_device_config config = ma_device_config_init(ma_device_type_playback);
78 config.playback.format = ma_format_f32; // Set to ma_format_unknown to use the device's native format.
79 config.playback.channels = 2; // Set to 0 to use the device's native channel count.
80 config.sampleRate = 48000; // Set to 0 to use the device's native sample rate.
81 config.dataCallback = data_callback; // This function will be called when miniaudio needs more data.
82 config.pUserData = pMyCustomData; // Can be accessed from the device object (device.pUserData).
83
84 ma_device device;
85 if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) {
86 return -1; // Failed to initialize the device.
87 }
88
89 ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually.
90
91 // Do something here. Probably your program's main loop.
92
93 ma_device_uninit(&device);
94 return 0;
95 }
96 ```
97
98In the example above, `data_callback()` is where audio data is written and read from the device.
99The idea is in playback mode you cause sound to be emitted from the speakers by writing audio data
100to the output buffer (`pOutput` in the example). In capture mode you read data from the input
101buffer (`pInput`) to extract sound captured by the microphone. The `frameCount` parameter tells you
102how many frames can be written to the output buffer and read from the input buffer. A "frame" is
103one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2
104samples: one for the left, one for the right. The channel count is defined by the device config.
105The size in bytes of an individual sample is defined by the sample format which is also specified
106in the device config. Multi-channel audio data is always interleaved, which means the samples for
107each frame are stored next to each other in memory. For example, in a stereo stream the first pair
108of samples will be the left and right samples for the first frame, the second pair of samples will
109be the left and right samples for the second frame, etc.
110
111The configuration of the device is defined by the `ma_device_config` structure. The config object
112is always initialized with `ma_device_config_init()`. It's important to always initialize the
113config with this function as it initializes it with logical defaults and ensures your program
114doesn't break when new members are added to the `ma_device_config` structure. The example above
115uses a fairly simple and standard device configuration. The call to `ma_device_config_init()` takes
116a single parameter, which is whether or not the device is a playback, capture, duplex or loopback
117device (loopback devices are not supported on all backends). The `config.playback.format` member
118sets the sample format which can be one of the following (all formats are native-endian):
119
120 +---------------+----------------------------------------+---------------------------+
121 | Symbol | Description | Range |
122 +---------------+----------------------------------------+---------------------------+
123 | ma_format_f32 | 32-bit floating point | [-1, 1] |
124 | ma_format_s16 | 16-bit signed integer | [-32768, 32767] |
125 | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] |
126 | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] |
127 | ma_format_u8 | 8-bit unsigned integer | [0, 255] |
128 +---------------+----------------------------------------+---------------------------+
129
130The `config.playback.channels` member sets the number of channels to use with the device. The
131channel count cannot exceed MA_MAX_CHANNELS. The `config.sampleRate` member sets the sample rate
132(which must be the same for both playback and capture in full-duplex configurations). This is
133usually set to 44100 or 48000, but can be set to anything. It's recommended to keep this between
1348000 and 384000, however.
135
136Note that leaving the format, channel count and/or sample rate at their default values will result
137in the internal device's native configuration being used which is useful if you want to avoid the
138overhead of miniaudio's automatic data conversion.
139
140In addition to the sample format, channel count and sample rate, the data callback and user data
141pointer are also set via the config. The user data pointer is not passed into the callback as a
142parameter, but is instead set to the `pUserData` member of `ma_device` which you can access
143directly since all miniaudio structures are transparent.
144
145Initializing the device is done with `ma_device_init()`. This will return a result code telling you
146what went wrong, if anything. On success it will return `MA_SUCCESS`. After initialization is
147complete the device will be in a stopped state. To start it, use `ma_device_start()`.
148Uninitializing the device will stop it, which is what the example above does, but you can also stop
149the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again.
150Note that it's important to never stop or start the device from inside the callback. This will
151result in a deadlock. Instead you set a variable or signal an event indicating that the device
152needs to stop and handle it in a different thread. The following APIs must never be called inside
153the callback:
154
155 ```c
156 ma_device_init()
157 ma_device_init_ex()
158 ma_device_uninit()
159 ma_device_start()
160 ma_device_stop()
161 ```
162
163You must never try uninitializing and reinitializing a device inside the callback. You must also
164never try to stop and start it from inside the callback. There are a few other things you shouldn't
165do in the callback depending on your requirements, however this isn't so much a thread-safety
166thing, but rather a real-time processing thing which is beyond the scope of this introduction.
167
168The example above demonstrates the initialization of a playback device, but it works exactly the
169same for capture. All you need to do is change the device type from `ma_device_type_playback` to
170`ma_device_type_capture` when setting up the config, like so:
171
172 ```c
173 ma_device_config config = ma_device_config_init(ma_device_type_capture);
174 config.capture.format = MY_FORMAT;
175 config.capture.channels = MY_CHANNEL_COUNT;
176 ```
177
178In the data callback you just read from the input buffer (`pInput` in the example above) and leave
179the output buffer alone (it will be set to NULL when the device type is set to
180`ma_device_type_capture`).
181
182These are the available device types and how you should handle the buffers in the callback:
183
184 +-------------------------+--------------------------------------------------------+
185 | Device Type | Callback Behavior |
186 +-------------------------+--------------------------------------------------------+
187 | ma_device_type_playback | Write to output buffer, leave input buffer untouched. |
188 | ma_device_type_capture | Read from input buffer, leave output buffer untouched. |
189 | ma_device_type_duplex | Read from input buffer, write to output buffer. |
190 | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. |
191 +-------------------------+--------------------------------------------------------+
192
193You will notice in the example above that the sample format and channel count is specified
194separately for playback and capture. This is to support different data formats between the playback
195and capture devices in a full-duplex system. An example may be that you want to capture audio data
196as a monaural stream (one channel), but output sound to a stereo speaker system. Note that if you
197use different formats between playback and capture in a full-duplex configuration you will need to
198convert the data yourself. There are functions available to help you do this which will be
199explained later.
200
201The example above did not specify a physical device to connect to which means it will use the
202operating system's default device. If you have multiple physical devices connected and you want to
203use a specific one you will need to specify the device ID in the configuration, like so:
204
205 ```c
206 config.playback.pDeviceID = pMyPlaybackDeviceID; // Only if requesting a playback or duplex device.
207 config.capture.pDeviceID = pMyCaptureDeviceID; // Only if requesting a capture, duplex or loopback device.
208 ```
209
210To retrieve the device ID you will need to perform device enumeration, however this requires the
211use of a new concept called the "context". Conceptually speaking the context sits above the device.
212There is one context to many devices. The purpose of the context is to represent the backend at a
213more global level and to perform operations outside the scope of an individual device. Mainly it is
214used for performing run-time linking against backend libraries, initializing backends and
215enumerating devices. The example below shows how to enumerate devices.
216
217 ```c
218 ma_context context;
219 if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) {
220 // Error.
221 }
222
223 ma_device_info* pPlaybackInfos;
224 ma_uint32 playbackCount;
225 ma_device_info* pCaptureInfos;
226 ma_uint32 captureCount;
227 if (ma_context_get_devices(&context, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount) != MA_SUCCESS) {
228 // Error.
229 }
230
231 // Loop over each device info and do something with it. Here we just print the name with their index. You may want
232 // to give the user the opportunity to choose which device they'd prefer.
233 for (ma_uint32 iDevice = 0; iDevice < playbackCount; iDevice += 1) {
234 printf("%d - %s\n", iDevice, pPlaybackInfos[iDevice].name);
235 }
236
237 ma_device_config config = ma_device_config_init(ma_device_type_playback);
238 config.playback.pDeviceID = &pPlaybackInfos[chosenPlaybackDeviceIndex].id;
239 config.playback.format = MY_FORMAT;
240 config.playback.channels = MY_CHANNEL_COUNT;
241 config.sampleRate = MY_SAMPLE_RATE;
242 config.dataCallback = data_callback;
243 config.pUserData = pMyCustomData;
244
245 ma_device device;
246 if (ma_device_init(&context, &config, &device) != MA_SUCCESS) {
247 // Error
248 }
249
250 ...
251
252 ma_device_uninit(&device);
253 ma_context_uninit(&context);
254 ```
255
256The first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`.
257The first parameter is a pointer to a list of `ma_backend` values which are used to override the
258default backend priorities. When this is NULL, as in this example, miniaudio's default priorities
259are used. The second parameter is the number of backends listed in the array pointed to by the
260first parameter. The third parameter is a pointer to a `ma_context_config` object which can be
261NULL, in which case defaults are used. The context configuration is used for setting the logging
262callback, custom memory allocation callbacks, user-defined data and some backend-specific
263configurations.
264
265Once the context has been initialized you can enumerate devices. In the example above we use the
266simpler `ma_context_get_devices()`, however you can also use a callback for handling devices by
267using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer
268to a pointer that will, upon output, be set to a pointer to a buffer containing a list of
269`ma_device_info` structures. You also provide a pointer to an unsigned integer that will receive
270the number of items in the returned buffer. Do not free the returned buffers as their memory is
271managed internally by miniaudio.
272
273The `ma_device_info` structure contains an `id` member which is the ID you pass to the device
274config. It also contains the name of the device which is useful for presenting a list of devices
275to the user via the UI.
276
277When creating your own context you will want to pass it to `ma_device_init()` when initializing the
278device. Passing in NULL, like we do in the first example, will result in miniaudio creating the
279context for you, which you don't want to do since you've already created a context. Note that
280internally the context is only tracked by it's pointer which means you must not change the location
281of the `ma_context` object. If this is an issue, consider using `malloc()` to allocate memory for
282the context.
283
284
2851.2. High Level API
286-------------------
287The high level API consists of three main parts:
288
289 * Resource management for loading and streaming sounds.
290 * A node graph for advanced mixing and effect processing.
291 * A high level "engine" that wraps around the resource manager and node graph.
292
293The resource manager (`ma_resource_manager`) is used for loading sounds. It supports loading sounds
294fully into memory and also streaming. It will also deal with reference counting for you which
295avoids the same sound being loaded multiple times.
296
297The node graph is used for mixing and effect processing. The idea is that you connect a number of
298nodes into the graph by connecting each node's outputs to another node's inputs. Each node can
299implement its own effect. By chaining nodes together, advanced mixing and effect processing can
300be achieved.
301
302The engine encapsulates both the resource manager and the node graph to create a simple, easy to
303use high level API. The resource manager and node graph APIs are covered in more later sections of
304this manual.
305
306The code below shows how you can initialize an engine using it's default configuration.
307
308 ```c
309 ma_result result;
310 ma_engine engine;
311
312 result = ma_engine_init(NULL, &engine);
313 if (result != MA_SUCCESS) {
314 return result; // Failed to initialize the engine.
315 }
316 ```
317
318This creates an engine instance which will initialize a device internally which you can access with
319`ma_engine_get_device()`. It will also initialize a resource manager for you which can be accessed
320with `ma_engine_get_resource_manager()`. The engine itself is a node graph (`ma_node_graph`) which
321means you can pass a pointer to the engine object into any of the `ma_node_graph` APIs (with a
322cast). Alternatively, you can use `ma_engine_get_node_graph()` instead of a cast.
323
324Note that all objects in miniaudio, including the `ma_engine` object in the example above, are
325transparent structures. There are no handles to opaque structures in miniaudio which means you need
326to be mindful of how you declare them. In the example above we are declaring it on the stack, but
327this will result in the struct being invalidated once the function encapsulating it returns. If
328allocating the engine on the heap is more appropriate, you can easily do so with a standard call
329to `malloc()` or whatever heap allocation routine you like:
330
331 ```c
332 ma_engine* pEngine = malloc(sizeof(*pEngine));
333 ```
334
335The `ma_engine` API uses the same config/init pattern used all throughout miniaudio. To configure
336an engine, you can fill out a `ma_engine_config` object and pass it into the first parameter of
337`ma_engine_init()`:
338
339 ```c
340 ma_result result;
341 ma_engine engine;
342 ma_engine_config engineConfig;
343
344 engineConfig = ma_engine_config_init();
345 engineConfig.pResourceManager = &myCustomResourceManager; // <-- Initialized as some earlier stage.
346
347 result = ma_engine_init(&engineConfig, &engine);
348 if (result != MA_SUCCESS) {
349 return result;
350 }
351 ```
352
353This creates an engine instance using a custom config. In this particular example it's showing how
354you can specify a custom resource manager rather than having the engine initialize one internally.
355This is particularly useful if you want to have multiple engine's share the same resource manager.
356
357The engine must be uninitialized with `ma_engine_uninit()` when it's no longer needed.
358
359By default the engine will be started, but nothing will be playing because no sounds have been
360initialized. The easiest but least flexible way of playing a sound is like so:
361
362 ```c
363 ma_engine_play_sound(&engine, "my_sound.wav", NULL);
364 ```
365
366This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the
367internal sound up for recycling. The last parameter is used to specify which sound group the sound
368should be associated with which will be explained later. This particular way of playing a sound is
369simple, but lacks flexibility and features. A more flexible way of playing a sound is to first
370initialize a sound:
371
372 ```c
373 ma_result result;
374 ma_sound sound;
375
376 result = ma_sound_init_from_file(&engine, "my_sound.wav", 0, NULL, NULL, &sound);
377 if (result != MA_SUCCESS) {
378 return result;
379 }
380
381 ma_sound_start(&sound);
382 ```
383
384This returns a `ma_sound` object which represents a single instance of the specified sound file. If
385you want to play the same file multiple times simultaneously, you need to create one sound for each
386instance.
387
388Sounds should be uninitialized with `ma_sound_uninit()`.
389
390Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with
391`ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use
392`ma_sound_seek_to_pcm_frame(&sound, 0)` to seek back to the start of a sound. By default, starting
393and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound
394the be started and/or stopped at a specific time. This can be done with the following functions:
395
396 ```c
397 ma_sound_set_start_time_in_pcm_frames()
398 ma_sound_set_start_time_in_milliseconds()
399 ma_sound_set_stop_time_in_pcm_frames()
400 ma_sound_set_stop_time_in_milliseconds()
401 ```
402
403The start/stop time needs to be specified based on the absolute timer which is controlled by the
404engine. The current global time in PCM frames can be retrieved with
405`ma_engine_get_time_in_pcm_frames()`. The engine's global time can be changed with
406`ma_engine_set_time_in_pcm_frames()` for synchronization purposes if required. Note that scheduling
407a start time still requires an explicit call to `ma_sound_start()` before anything will play:
408
409 ```c
410 ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2);
411 ma_sound_start(&sound);
412 ```
413
414The third parameter of `ma_sound_init_from_file()` is a set of flags that control how the sound be
415loaded and a few options on which features should be enabled for that sound. By default, the sound
416is synchronously loaded fully into memory straight from the file system without any kind of
417decoding. If you want to decode the sound before storing it in memory, you need to specify the
418`MA_SOUND_FLAG_DECODE` flag. This is useful if you want to incur the cost of decoding at an earlier
419stage, such as a loading stage. Without this option, decoding will happen dynamically at mixing
420time which might be too expensive on the audio thread.
421
422If you want to load the sound asynchronously, you can specify the `MA_SOUND_FLAG_ASYNC` flag. This
423will result in `ma_sound_init_from_file()` returning quickly, but the sound will not start playing
424until the sound has had some audio decoded.
425
426The fourth parameter is a pointer to sound group. A sound group is used as a mechanism to organise
427sounds into groups which have their own effect processing and volume control. An example is a game
428which might have separate groups for sfx, voice and music. Each of these groups have their own
429independent volume control. Use `ma_sound_group_init()` or `ma_sound_group_init_ex()` to initialize
430a sound group.
431
432Sounds and sound groups are nodes in the engine's node graph and can be plugged into any `ma_node`
433API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex
434effect chains.
435
436A sound can have its volume changed with `ma_sound_set_volume()`. If you prefer decibel volume
437control you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear.
438
439Panning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know
440a sound will never have its pitch changed with `ma_sound_set_pitch()` or via the doppler effect,
441you can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization.
442
443By default, sounds and sound groups have spatialization enabled. If you don't ever want to
444spatialize your sounds, initialize the sound with the `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. The
445spatialization model is fairly simple and is roughly on feature parity with OpenAL. HRTF and
446environmental occlusion are not currently supported, but planned for the future. The supported
447features include:
448
449 * Sound and listener positioning and orientation with cones
450 * Attenuation models: none, inverse, linear and exponential
451 * Doppler effect
452
453Sounds can be faded in and out with `ma_sound_set_fade_in_pcm_frames()`.
454
455To check if a sound is currently playing, you can use `ma_sound_is_playing()`. To check if a sound
456is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled with
457`ma_sound_set_looping()`. Use `ma_sound_is_looping()` to check whether or not the sound is looping.
458
459
460
4612. Building
462===========
463miniaudio should work cleanly out of the box without the need to download or install any
464dependencies. See below for platform-specific details.
465
466Note that GCC and Clang require `-msse2`, `-mavx2`, etc. for SIMD optimizations.
467
468If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`,
469etc. you need to link with `-latomic`.
470
471
4722.1. Windows
473------------
474The Windows build should compile cleanly on all popular compilers without the need to configure any
475include paths nor link to any libraries.
476
477The UWP build may require linking to mmdevapi.lib if you get errors about an unresolved external
478symbol for `ActivateAudioInterfaceAsync()`.
479
480
4812.2. macOS and iOS
482------------------
483The macOS build should compile cleanly without the need to download any dependencies nor link to
484any libraries or frameworks. The iOS build needs to be compiled as Objective-C and will need to
485link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling
486through the command line requires linking to `-lpthread` and `-lm`.
487
488Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's
489notarization process. To fix this there are two options. The first is to compile with
490`-DMA_NO_RUNTIME_LINKING` which in turn will require linking with
491`-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`. If you get errors about
492AudioToolbox, try with `-framework AudioUnit` instead. You may get this when using older versions
493of iOS. Alternatively, if you would rather keep using runtime linking you can add the following to
494your entitlements.xcent file:
495
496 ```
497 <key>com.apple.security.cs.allow-dyld-environment-variables</key>
498 <true/>
499 <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
500 <true/>
501 ```
502
503See this discussion for more info: https://github.com/mackron/miniaudio/issues/203.
504
505
5062.3. Linux
507----------
508The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any
509development packages. You may need to link with `-latomic` if you're compiling for 32-bit ARM.
510
511
5122.4. BSD
513--------
514The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses
515sndio and FreeBSD uses OSS. You may need to link with `-latomic` if you're compiling for 32-bit
516ARM.
517
518
5192.5. Android
520------------
521AAudio is the highest priority backend on Android. This should work out of the box without needing
522any kind of compiler configuration. Support for AAudio starts with Android 8 which means older
523versions will fall back to OpenSL|ES which requires API level 16+.
524
525There have been reports that the OpenSL|ES backend fails to initialize on some Android based
526devices due to `dlopen()` failing to open "libOpenSLES.so". If this happens on your platform
527you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES.
528
529
5302.6. Emscripten
531---------------
532The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box.
533You cannot use `-std=c*` compiler flags, nor `-ansi`.
534
535You can enable the use of AudioWorkets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling
536with the following options:
537
538 -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY
539
540An example for compiling with AudioWorklet support might look like this:
541
542 emcc program.c -o bin/program.html -DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY
543
544To run locally, you'll need to use emrun:
545
546 emrun bin/program.html
547
548
549
5502.7. Build Options
551------------------
552`#define` these options before including miniaudio.c, or pass them as compiler flags:
553
554 +----------------------------------+--------------------------------------------------------------------+
555 | Option | Description |
556 +----------------------------------+--------------------------------------------------------------------+
557 | MA_NO_WASAPI | Disables the WASAPI backend. |
558 +----------------------------------+--------------------------------------------------------------------+
559 | MA_NO_DSOUND | Disables the DirectSound backend. |
560 +----------------------------------+--------------------------------------------------------------------+
561 | MA_NO_WINMM | Disables the WinMM backend. |
562 +----------------------------------+--------------------------------------------------------------------+
563 | MA_NO_ALSA | Disables the ALSA backend. |
564 +----------------------------------+--------------------------------------------------------------------+
565 | MA_NO_PULSEAUDIO | Disables the PulseAudio backend. |
566 +----------------------------------+--------------------------------------------------------------------+
567 | MA_NO_JACK | Disables the JACK backend. |
568 +----------------------------------+--------------------------------------------------------------------+
569 | MA_NO_COREAUDIO | Disables the Core Audio backend. |
570 +----------------------------------+--------------------------------------------------------------------+
571 | MA_NO_SNDIO | Disables the sndio backend. |
572 +----------------------------------+--------------------------------------------------------------------+
573 | MA_NO_AUDIO4 | Disables the audio(4) backend. |
574 +----------------------------------+--------------------------------------------------------------------+
575 | MA_NO_OSS | Disables the OSS backend. |
576 +----------------------------------+--------------------------------------------------------------------+
577 | MA_NO_AAUDIO | Disables the AAudio backend. |
578 +----------------------------------+--------------------------------------------------------------------+
579 | MA_NO_OPENSL | Disables the OpenSL|ES backend. |
580 +----------------------------------+--------------------------------------------------------------------+
581 | MA_NO_WEBAUDIO | Disables the Web Audio backend. |
582 +----------------------------------+--------------------------------------------------------------------+
583 | MA_NO_CUSTOM | Disables support for custom backends. |
584 +----------------------------------+--------------------------------------------------------------------+
585 | MA_NO_NULL | Disables the null backend. |
586 +----------------------------------+--------------------------------------------------------------------+
587 | MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to |
588 | | enable specific backends. |
589 +----------------------------------+--------------------------------------------------------------------+
590 | MA_ENABLE_WASAPI | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
591 | | enable the WASAPI backend. |
592 +----------------------------------+--------------------------------------------------------------------+
593 | MA_ENABLE_DSOUND | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
594 | | enable the DirectSound backend. |
595 +----------------------------------+--------------------------------------------------------------------+
596 | MA_ENABLE_WINMM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
597 | | enable the WinMM backend. |
598 +----------------------------------+--------------------------------------------------------------------+
599 | MA_ENABLE_ALSA | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
600 | | enable the ALSA backend. |
601 +----------------------------------+--------------------------------------------------------------------+
602 | MA_ENABLE_PULSEAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
603 | | enable the PulseAudio backend. |
604 +----------------------------------+--------------------------------------------------------------------+
605 | MA_ENABLE_JACK | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
606 | | enable the JACK backend. |
607 +----------------------------------+--------------------------------------------------------------------+
608 | MA_ENABLE_COREAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
609 | | enable the Core Audio backend. |
610 +----------------------------------+--------------------------------------------------------------------+
611 | MA_ENABLE_SNDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
612 | | enable the sndio backend. |
613 +----------------------------------+--------------------------------------------------------------------+
614 | MA_ENABLE_AUDIO4 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
615 | | enable the audio(4) backend. |
616 +----------------------------------+--------------------------------------------------------------------+
617 | MA_ENABLE_OSS | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
618 | | enable the OSS backend. |
619 +----------------------------------+--------------------------------------------------------------------+
620 | MA_ENABLE_AAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
621 | | enable the AAudio backend. |
622 +----------------------------------+--------------------------------------------------------------------+
623 | MA_ENABLE_OPENSL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
624 | | enable the OpenSL|ES backend. |
625 +----------------------------------+--------------------------------------------------------------------+
626 | MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
627 | | enable the Web Audio backend. |
628 +----------------------------------+--------------------------------------------------------------------+
629 | MA_ENABLE_CUSTOM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
630 | | enable custom backends. |
631 +----------------------------------+--------------------------------------------------------------------+
632 | MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
633 | | enable the null backend. |
634 +----------------------------------+--------------------------------------------------------------------+
635 | MA_NO_DECODING | Disables decoding APIs. |
636 +----------------------------------+--------------------------------------------------------------------+
637 | MA_NO_ENCODING | Disables encoding APIs. |
638 +----------------------------------+--------------------------------------------------------------------+
639 | MA_NO_WAV | Disables the built-in WAV decoder and encoder. |
640 +----------------------------------+--------------------------------------------------------------------+
641 | MA_NO_FLAC | Disables the built-in FLAC decoder. |
642 +----------------------------------+--------------------------------------------------------------------+
643 | MA_NO_MP3 | Disables the built-in MP3 decoder. |
644 +----------------------------------+--------------------------------------------------------------------+
645 | MA_NO_DEVICE_IO | Disables playback and recording. This will disable `ma_context` |
646 | | and `ma_device` APIs. This is useful if you only want to use |
647 | | miniaudio's data conversion and/or decoding APIs. |
648 +----------------------------------+--------------------------------------------------------------------+
649 | MA_NO_RESOURCE_MANAGER | Disables the resource manager. When using the engine this will |
650 | | also disable the following functions: |
651 | | |
652 | | ``` |
653 | | ma_sound_init_from_file() |
654 | | ma_sound_init_from_file_w() |
655 | | ma_sound_init_copy() |
656 | | ma_engine_play_sound_ex() |
657 | | ma_engine_play_sound() |
658 | | ``` |
659 | | |
660 | | The only way to initialize a `ma_sound` object is to initialize it |
661 | | from a data source. |
662 +----------------------------------+--------------------------------------------------------------------+
663 | MA_NO_NODE_GRAPH | Disables the node graph API. This will also disable the engine API |
664 | | because it depends on the node graph. |
665 +----------------------------------+--------------------------------------------------------------------+
666 | MA_NO_ENGINE | Disables the engine API. |
667 +----------------------------------+--------------------------------------------------------------------+
668 | MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and |
669 | | `ma_event` APIs. This option is useful if you only need to use |
670 | | miniaudio for data conversion, decoding and/or encoding. Some |
671 | | families of APIs require threading which means the following |
672 | | options must also be set: |
673 | | |
674 | | ``` |
675 | | MA_NO_DEVICE_IO |
676 | | ``` |
677 +----------------------------------+--------------------------------------------------------------------+
678 | MA_NO_GENERATION | Disables generation APIs such a `ma_waveform` and `ma_noise`. |
679 +----------------------------------+--------------------------------------------------------------------+
680 | MA_NO_SSE2 | Disables SSE2 optimizations. |
681 +----------------------------------+--------------------------------------------------------------------+
682 | MA_NO_AVX2 | Disables AVX2 optimizations. |
683 +----------------------------------+--------------------------------------------------------------------+
684 | MA_NO_NEON | Disables NEON optimizations. |
685 +----------------------------------+--------------------------------------------------------------------+
686 | MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's |
687 | | notarization process. When enabling this, you may need to avoid |
688 | | using `-std=c89` or `-std=c99` on Linux builds or else you may end |
689 | | up with compilation errors due to conflicts with `timespec` and |
690 | | `timeval` data types. |
691 | | |
692 | | You may need to enable this if your target platform does not allow |
693 | | runtime linking via `dlopen()`. |
694 +----------------------------------+--------------------------------------------------------------------+
695 | MA_USE_STDINT | (Pass this in as a compiler flag. Do not `#define` this before |
696 | | miniaudio.c) Forces the use of stdint.h for sized types. |
697 +----------------------------------+--------------------------------------------------------------------+
698 | MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). |
699 +----------------------------------+--------------------------------------------------------------------+
700 | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to |
701 | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. |
702 +----------------------------------+--------------------------------------------------------------------+
703 | MA_FORCE_UWP | Windows only. Affects only the WASAPI backend. Will force the |
704 | | WASAPI backend to use the UWP code path instead of the regular |
705 | | desktop path. This is normally auto-detected and should rarely be |
706 | | needed to be used explicitly, but can be useful for debugging. |
707 +----------------------------------+--------------------------------------------------------------------+
708 | MA_ON_THREAD_ENTRY | Defines some code that will be executed as soon as an internal |
709 | | miniaudio-managed thread is created. This will be the first thing |
710 | | to be executed by the thread entry point. |
711 +----------------------------------+--------------------------------------------------------------------+
712 | MA_ON_THREAD_EXIT | Defines some code that will be executed from the entry point of an |
713 | | internal miniaudio-managed thread upon exit. This will be the last |
714 | | thing to be executed before the thread's entry point exits. |
715 +----------------------------------+--------------------------------------------------------------------+
716 | MA_THREAD_DEFAULT_STACK_SIZE | If set, specifies the default stack size used by miniaudio-managed |
717 | | threads. |
718 +----------------------------------+--------------------------------------------------------------------+
719 | MA_API | Controls how public APIs should be decorated. Default is `extern`. |
720 +----------------------------------+--------------------------------------------------------------------+
721
722
7233. Definitions
724==============
725This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity
726in the use of terms throughout the audio space, so this section is intended to clarify how miniaudio
727uses each term.
728
7293.1. Sample
730-----------
731A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit
732floating point number.
733
7343.2. Frame / PCM Frame
735----------------------
736A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2
737samples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame"
738and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame.
739If ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always
740clarify what it's referring to with something like "FLAC frame".
741
7423.3. Channel
743------------
744A stream of monaural audio that is emitted from an individual speaker in a speaker system, or
745received from an individual microphone in a microphone system. A stereo stream has two channels (a
746left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio
747systems refer to a channel as a complex audio stream that's mixed with other channels to produce
748the final mix - this is completely different to miniaudio's use of the term "channel" and should
749not be confused.
750
7513.4. Sample Rate
752----------------
753The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number
754of PCM frames that are processed per second.
755
7563.5. Formats
757------------
758Throughout miniaudio you will see references to different sample formats:
759
760 +---------------+----------------------------------------+---------------------------+
761 | Symbol | Description | Range |
762 +---------------+----------------------------------------+---------------------------+
763 | ma_format_f32 | 32-bit floating point | [-1, 1] |
764 | ma_format_s16 | 16-bit signed integer | [-32768, 32767] |
765 | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] |
766 | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] |
767 | ma_format_u8 | 8-bit unsigned integer | [0, 255] |
768 +---------------+----------------------------------------+---------------------------+
769
770All formats are native-endian.
771
772
773
7744. Data Sources
775===============
776The data source abstraction in miniaudio is used for retrieving audio data from some source. A few
777examples include `ma_decoder`, `ma_noise` and `ma_waveform`. You will need to be familiar with data
778sources in order to make sense of some of the higher level concepts in miniaudio.
779
780The `ma_data_source` API is a generic interface for reading from a data source. Any object that
781implements the data source interface can be plugged into any `ma_data_source` function.
782
783To read data from a data source:
784
785 ```c
786 ma_result result;
787 ma_uint64 framesRead;
788
789 result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead);
790 if (result != MA_SUCCESS) {
791 return result; // Failed to read data from the data source.
792 }
793 ```
794
795If you don't need the number of frames that were successfully read you can pass in `NULL` to the
796`pFramesRead` parameter. If this returns a value less than the number of frames requested it means
797the end of the file has been reached. `MA_AT_END` will be returned only when the number of frames
798read is 0.
799
800When calling any data source function, with the exception of `ma_data_source_init()` and
801`ma_data_source_uninit()`, you can pass in any object that implements a data source. For example,
802you could plug in a decoder like so:
803
804 ```c
805 ma_result result;
806 ma_uint64 framesRead;
807 ma_decoder decoder; // <-- This would be initialized with `ma_decoder_init_*()`.
808
809 result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead);
810 if (result != MA_SUCCESS) {
811 return result; // Failed to read data from the decoder.
812 }
813 ```
814
815If you want to seek forward you can pass in `NULL` to the `pFramesOut` parameter. Alternatively you
816can use `ma_data_source_seek_pcm_frames()`.
817
818To seek to a specific PCM frame:
819
820 ```c
821 result = ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex);
822 if (result != MA_SUCCESS) {
823 return result; // Failed to seek to PCM frame.
824 }
825 ```
826
827You can retrieve the total length of a data source in PCM frames, but note that some data sources
828may not have the notion of a length, such as noise and waveforms, and others may just not have a
829way of determining the length such as some decoders. To retrieve the length:
830
831 ```c
832 ma_uint64 length;
833
834 result = ma_data_source_get_length_in_pcm_frames(pDataSource, &length);
835 if (result != MA_SUCCESS) {
836 return result; // Failed to retrieve the length.
837 }
838 ```
839
840Care should be taken when retrieving the length of a data source where the underlying decoder is
841pulling data from a data stream with an undefined length, such as internet radio or some kind of
842broadcast. If you do this, `ma_data_source_get_length_in_pcm_frames()` may never return.
843
844The current position of the cursor in PCM frames can also be retrieved:
845
846 ```c
847 ma_uint64 cursor;
848
849 result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor);
850 if (result != MA_SUCCESS) {
851 return result; // Failed to retrieve the cursor.
852 }
853 ```
854
855You will often need to know the data format that will be returned after reading. This can be
856retrieved like so:
857
858 ```c
859 ma_format format;
860 ma_uint32 channels;
861 ma_uint32 sampleRate;
862 ma_channel channelMap[MA_MAX_CHANNELS];
863
864 result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS);
865 if (result != MA_SUCCESS) {
866 return result; // Failed to retrieve data format.
867 }
868 ```
869
870If you do not need a specific data format property, just pass in NULL to the respective parameter.
871
872There may be cases where you want to implement something like a sound bank where you only want to
873read data within a certain range of the underlying data. To do this you can use a range:
874
875 ```c
876 result = ma_data_source_set_range_in_pcm_frames(pDataSource, rangeBegInFrames, rangeEndInFrames);
877 if (result != MA_SUCCESS) {
878 return result; // Failed to set the range.
879 }
880 ```
881
882This is useful if you have a sound bank where many sounds are stored in the same file and you want
883the data source to only play one of those sub-sounds. Note that once the range is set, everything
884that takes a position, such as cursors and loop points, should always be relatvie to the start of
885the range. When the range is set, any previously defined loop point will be reset.
886
887Custom loop points can also be used with data sources. By default, data sources will loop after
888they reach the end of the data source, but if you need to loop at a specific location, you can do
889the following:
890
891 ```c
892 result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames);
893 if (result != MA_SUCCESS) {
894 return result; // Failed to set the loop point.
895 }
896 ```
897
898The loop point is relative to the current range.
899
900It's sometimes useful to chain data sources together so that a seamless transition can be achieved.
901To do this, you can use chaining:
902
903 ```c
904 ma_decoder decoder1;
905 ma_decoder decoder2;
906
907 // ... initialize decoders with ma_decoder_init_*() ...
908
909 result = ma_data_source_set_next(&decoder1, &decoder2);
910 if (result != MA_SUCCESS) {
911 return result; // Failed to set the next data source.
912 }
913
914 result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead);
915 if (result != MA_SUCCESS) {
916 return result; // Failed to read from the decoder.
917 }
918 ```
919
920In the example above we're using decoders. When reading from a chain, you always want to read from
921the top level data source in the chain. In the example above, `decoder1` is the top level data
922source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any
923gaps.
924
925Note that when looping is enabled, only the current data source will be looped. You can loop the
926entire chain by linking in a loop like so:
927
928 ```c
929 ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2
930 ma_data_source_set_next(&decoder2, &decoder1); // decoder2 -> decoder1 (loop back to the start).
931 ```
932
933Note that setting up chaining is not thread safe, so care needs to be taken if you're dynamically
934changing links while the audio thread is in the middle of reading.
935
936Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple
937instances of the same sound simultaneously. This can be extremely inefficient depending on the type
938of data source and can result in glitching due to subtle changes to the state of internal filters.
939Instead, initialize multiple data sources for each instance.
940
941
9424.1. Custom Data Sources
943------------------------
944You can implement a custom data source by implementing the functions in `ma_data_source_vtable`.
945Your custom object must have `ma_data_source_base` as it's first member:
946
947 ```c
948 struct my_data_source
949 {
950 ma_data_source_base base;
951 ...
952 };
953 ```
954
955In your initialization routine, you need to call `ma_data_source_init()` in order to set up the
956base object (`ma_data_source_base`):
957
958 ```c
959 static ma_result my_data_source_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
960 {
961 // Read data here. Output in the same format returned by my_data_source_get_data_format().
962 }
963
964 static ma_result my_data_source_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
965 {
966 // Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported.
967 }
968
969 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)
970 {
971 // Return the format of the data here.
972 }
973
974 static ma_result my_data_source_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
975 {
976 // 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.
977 }
978
979 static ma_result my_data_source_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
980 {
981 // 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.
982 }
983
984 static ma_data_source_vtable g_my_data_source_vtable =
985 {
986 my_data_source_read,
987 my_data_source_seek,
988 my_data_source_get_data_format,
989 my_data_source_get_cursor,
990 my_data_source_get_length
991 };
992
993 ma_result my_data_source_init(my_data_source* pMyDataSource)
994 {
995 ma_result result;
996 ma_data_source_config baseConfig;
997
998 baseConfig = ma_data_source_config_init();
999 baseConfig.vtable = &g_my_data_source_vtable;
1000
1001 result = ma_data_source_init(&baseConfig, &pMyDataSource->base);
1002 if (result != MA_SUCCESS) {
1003 return result;
1004 }
1005
1006 // ... do the initialization of your custom data source here ...
1007
1008 return MA_SUCCESS;
1009 }
1010
1011 void my_data_source_uninit(my_data_source* pMyDataSource)
1012 {
1013 // ... do the uninitialization of your custom data source here ...
1014
1015 // You must uninitialize the base data source.
1016 ma_data_source_uninit(&pMyDataSource->base);
1017 }
1018 ```
1019
1020Note that `ma_data_source_init()` and `ma_data_source_uninit()` are never called directly outside
1021of the custom data source. It's up to the custom data source itself to call these within their own
1022init/uninit functions.
1023
1024
1025
10265. Engine
1027=========
1028The `ma_engine` API is a high level API for managing and mixing sounds and effect processing. The
1029`ma_engine` object encapsulates a resource manager and a node graph, both of which will be
1030explained in more detail later.
1031
1032Sounds are called `ma_sound` and are created from an engine. Sounds can be associated with a mixing
1033group called `ma_sound_group` which are also created from the engine. Both `ma_sound` and
1034`ma_sound_group` objects are nodes within the engine's node graph.
1035
1036When the engine is initialized, it will normally create a device internally. If you would rather
1037manage the device yourself, you can do so and just pass a pointer to it via the engine config when
1038you initialize the engine. You can also just use the engine without a device, which again can be
1039configured via the engine config.
1040
1041The most basic way to initialize the engine is with a default config, like so:
1042
1043 ```c
1044 ma_result result;
1045 ma_engine engine;
1046
1047 result = ma_engine_init(NULL, &engine);
1048 if (result != MA_SUCCESS) {
1049 return result; // Failed to initialize the engine.
1050 }
1051 ```
1052
1053This will result in the engine initializing a playback device using the operating system's default
1054device. This will be sufficient for many use cases, but if you need more flexibility you'll want to
1055configure the engine with an engine config:
1056
1057 ```c
1058 ma_result result;
1059 ma_engine engine;
1060 ma_engine_config engineConfig;
1061
1062 engineConfig = ma_engine_config_init();
1063 engineConfig.pDevice = &myDevice;
1064
1065 result = ma_engine_init(&engineConfig, &engine);
1066 if (result != MA_SUCCESS) {
1067 return result; // Failed to initialize the engine.
1068 }
1069 ```
1070
1071In the example above we're passing in a pre-initialized device. Since the caller is the one in
1072control of the device's data callback, it's their responsibility to manually call
1073`ma_engine_read_pcm_frames()` from inside their data callback:
1074
1075 ```c
1076 void playback_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
1077 {
1078 ma_engine_read_pcm_frames(&g_Engine, pOutput, frameCount, NULL);
1079 }
1080 ```
1081
1082You can also use the engine independent of a device entirely:
1083
1084 ```c
1085 ma_result result;
1086 ma_engine engine;
1087 ma_engine_config engineConfig;
1088
1089 engineConfig = ma_engine_config_init();
1090 engineConfig.noDevice = MA_TRUE;
1091 engineConfig.channels = 2; // Must be set when not using a device.
1092 engineConfig.sampleRate = 48000; // Must be set when not using a device.
1093
1094 result = ma_engine_init(&engineConfig, &engine);
1095 if (result != MA_SUCCESS) {
1096 return result; // Failed to initialize the engine.
1097 }
1098 ```
1099
1100Note that when you're not using a device, you must set the channel count and sample rate in the
1101config or else miniaudio won't know what to use (miniaudio will use the device to determine this
1102normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio
1103data from the engine. This kind of setup is useful if you want to do something like offline
1104processing or want to use a different audio system for playback such as SDL.
1105
1106When a sound is loaded it goes through a resource manager. By default the engine will initialize a
1107resource manager internally, but you can also specify a pre-initialized resource manager:
1108
1109 ```c
1110 ma_result result;
1111 ma_engine engine1;
1112 ma_engine engine2;
1113 ma_engine_config engineConfig;
1114
1115 engineConfig = ma_engine_config_init();
1116 engineConfig.pResourceManager = &myResourceManager;
1117
1118 ma_engine_init(&engineConfig, &engine1);
1119 ma_engine_init(&engineConfig, &engine2);
1120 ```
1121
1122In this example we are initializing two engines, both of which are sharing the same resource
1123manager. This is especially useful for saving memory when loading the same file across multiple
1124engines. If you were not to use a shared resource manager, each engine instance would use their own
1125which would result in any sounds that are used between both engine's being loaded twice. By using
1126a shared resource manager, it would only be loaded once. Using multiple engine's is useful when you
1127need to output to multiple playback devices, such as in a local multiplayer game where each player
1128is using their own set of headphones.
1129
1130By default an engine will be in a started state. To make it so the engine is not automatically
1131started you can configure it as such:
1132
1133 ```c
1134 engineConfig.noAutoStart = MA_TRUE;
1135
1136 // The engine will need to be started manually.
1137 ma_engine_start(&engine);
1138
1139 // Later on the engine can be stopped with ma_engine_stop().
1140 ma_engine_stop(&engine);
1141 ```
1142
1143The concept of starting or stopping an engine is only relevant when using the engine with a
1144device. Attempting to start or stop an engine that is not associated with a device will result in
1145`MA_INVALID_OPERATION`.
1146
1147The master volume of the engine can be controlled with `ma_engine_set_volume()` which takes a
1148linear scale, with 0 resulting in silence and anything above 1 resulting in amplification. If you
1149prefer decibel based volume control, use `ma_volume_db_to_linear()` to convert from dB to linear.
1150
1151When a sound is spatialized, it is done so relative to a listener. An engine can be configured to
1152have multiple listeners which can be configured via the config:
1153
1154 ```c
1155 engineConfig.listenerCount = 2;
1156 ```
1157
1158The maximum number of listeners is restricted to `MA_ENGINE_MAX_LISTENERS`. By default, when a
1159sound is spatialized, it will be done so relative to the closest listener. You can also pin a sound
1160to a specific listener which will be explained later. Listener's have a position, direction, cone,
1161and velocity (for doppler effect). A listener is referenced by an index, the meaning of which is up
1162to the caller (the index is 0 based and cannot go beyond the listener count, minus 1). The
1163position, direction and velocity are all specified in absolute terms:
1164
1165 ```c
1166 ma_engine_listener_set_position(&engine, listenerIndex, worldPosX, worldPosY, worldPosZ);
1167 ```
1168
1169The direction of the listener represents it's forward vector. The listener's up vector can also be
1170specified and defaults to +1 on the Y axis.
1171
1172 ```c
1173 ma_engine_listener_set_direction(&engine, listenerIndex, forwardX, forwardY, forwardZ);
1174 ma_engine_listener_set_world_up(&engine, listenerIndex, 0, 1, 0);
1175 ```
1176
1177The engine supports directional attenuation. The listener can have a cone the controls how sound is
1178attenuated based on the listener's direction. When a sound is between the inner and outer cones, it
1179will be attenuated between 1 and the cone's outer gain:
1180
1181 ```c
1182 ma_engine_listener_set_cone(&engine, listenerIndex, innerAngleInRadians, outerAngleInRadians, outerGain);
1183 ```
1184
1185When a sound is inside the inner code, no directional attenuation is applied. When the sound is
1186outside of the outer cone, the attenuation will be set to `outerGain` in the example above. When
1187the sound is in between the inner and outer cones, the attenuation will be interpolated between 1
1188and the outer gain.
1189
1190The engine's coordinate system follows the OpenGL coordinate system where positive X points right,
1191positive Y points up and negative Z points forward.
1192
1193The simplest and least flexible way to play a sound is like so:
1194
1195 ```c
1196 ma_engine_play_sound(&engine, "my_sound.wav", pGroup);
1197 ```
1198
1199This is a "fire and forget" style of function. The engine will manage the `ma_sound` object
1200internally. When the sound finishes playing, it'll be put up for recycling. For more flexibility
1201you'll want to initialize a sound object:
1202
1203 ```c
1204 ma_sound sound;
1205
1206 result = ma_sound_init_from_file(&engine, "my_sound.wav", flags, pGroup, NULL, &sound);
1207 if (result != MA_SUCCESS) {
1208 return result; // Failed to load sound.
1209 }
1210 ```
1211
1212Sounds need to be uninitialized with `ma_sound_uninit()`.
1213
1214The example above loads a sound from a file. If the resource manager has been disabled you will not
1215be able to use this function and instead you'll need to initialize a sound directly from a data
1216source:
1217
1218 ```c
1219 ma_sound sound;
1220
1221 result = ma_sound_init_from_data_source(&engine, &dataSource, flags, pGroup, &sound);
1222 if (result != MA_SUCCESS) {
1223 return result;
1224 }
1225 ```
1226
1227Each `ma_sound` object represents a single instance of the sound. If you want to play the same
1228sound multiple times at the same time, you need to initialize a separate `ma_sound` object.
1229
1230For the most flexibility when initializing sounds, use `ma_sound_init_ex()`. This uses miniaudio's
1231standard config/init pattern:
1232
1233 ```c
1234 ma_sound sound;
1235 ma_sound_config soundConfig;
1236
1237 soundConfig = ma_sound_config_init();
1238 soundConfig.pFilePath = NULL; // Set this to load from a file path.
1239 soundConfig.pDataSource = NULL; // Set this to initialize from an existing data source.
1240 soundConfig.pInitialAttachment = &someNodeInTheNodeGraph;
1241 soundConfig.initialAttachmentInputBusIndex = 0;
1242 soundConfig.channelsIn = 1;
1243 soundConfig.channelsOut = 0; // Set to 0 to use the engine's native channel count.
1244
1245 result = ma_sound_init_ex(&soundConfig, &sound);
1246 if (result != MA_SUCCESS) {
1247 return result;
1248 }
1249 ```
1250
1251In the example above, the sound is being initialized without a file nor a data source. This is
1252valid, in which case the sound acts as a node in the middle of the node graph. This means you can
1253connect other sounds to this sound and allow it to act like a sound group. Indeed, this is exactly
1254what a `ma_sound_group` is.
1255
1256When loading a sound, you specify a set of flags that control how the sound is loaded and what
1257features are enabled for that sound. When no flags are set, the sound will be fully loaded into
1258memory in exactly the same format as how it's stored on the file system. The resource manager will
1259allocate a block of memory and then load the file directly into it. When reading audio data, it
1260will be decoded dynamically on the fly. In order to save processing time on the audio thread, it
1261might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_FLAG_DECODE` flag:
1262
1263 ```c
1264 ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE, pGroup, NULL, &sound);
1265 ```
1266
1267By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until
1268the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously
1269by specifying the `MA_SOUND_FLAG_ASYNC` flag:
1270
1271 ```c
1272 ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound);
1273 ```
1274
1275This will result in `ma_sound_init_*()` returning quickly, but the sound won't yet have been fully
1276loaded. When you start the sound, it won't output anything until some sound is available. The sound
1277will start outputting audio before the sound has been fully decoded when the `MA_SOUND_FLAG_DECODE`
1278is specified.
1279
1280If you need to wait for an asynchronously loaded sound to be fully loaded, you can use a fence. A
1281fence in miniaudio is a simple synchronization mechanism which simply blocks until it's internal
1282counter hit's zero. You can specify a fence like so:
1283
1284 ```c
1285 ma_result result;
1286 ma_fence fence;
1287 ma_sound sounds[4];
1288
1289 result = ma_fence_init(&fence);
1290 if (result != MA_SUCCESS) {
1291 return result;
1292 }
1293
1294 // Load some sounds asynchronously.
1295 for (int iSound = 0; iSound < 4; iSound += 1) {
1296 ma_sound_init_from_file(&engine, mySoundFilesPaths[iSound], MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, &fence, &sounds[iSound]);
1297 }
1298
1299 // ... do some other stuff here in the mean time ...
1300
1301 // Wait for all sounds to finish loading.
1302 ma_fence_wait(&fence);
1303 ```
1304
1305If loading the entire sound into memory is prohibitive, you can also configure the engine to stream
1306the audio data:
1307
1308 ```c
1309 ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_STREAM, pGroup, NULL, &sound);
1310 ```
1311
1312When streaming sounds, 2 seconds worth of audio data is stored in memory. Although it should work
1313fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music
1314tracks in games.
1315
1316When loading a sound from a file path, the engine will reference count the file to prevent it from
1317being loaded if it's already in memory. When you uninitialize a sound, the reference count will be
1318decremented, and if it hits zero, the sound will be unloaded from memory. This reference counting
1319system is not used for streams. The engine will use a 64-bit hash of the file name when comparing
1320file paths which means there's a small chance you might encounter a name collision. If this is an
1321issue, you'll need to use a different name for one of the colliding file paths, or just not load
1322from files and instead load from a data source.
1323
1324You can use `ma_sound_init_copy()` to initialize a copy of another sound. Note, however, that this
1325only works for sounds that were initialized with `ma_sound_init_from_file()` and without the
1326`MA_SOUND_FLAG_STREAM` flag.
1327
1328When you initialize a sound, if you specify a sound group the sound will be attached to that group
1329automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint.
1330If you would instead rather leave the sound unattached by default, you can specify the
1331`MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node
1332graph.
1333
1334Sounds are not started by default. To start a sound, use `ma_sound_start()`. Stop a sound with
1335`ma_sound_stop()`.
1336
1337Sounds can have their volume controlled with `ma_sound_set_volume()` in the same way as the
1338engine's master volume.
1339
1340Sounds support stereo panning and pitching. Set the pan with `ma_sound_set_pan()`. Setting the pan
1341to 0 will result in an unpanned sound. Setting it to -1 will shift everything to the left, whereas
1342+1 will shift it to the right. The pitch can be controlled with `ma_sound_set_pitch()`. A larger
1343value will result in a higher pitch. The pitch must be greater than 0.
1344
1345The engine supports 3D spatialization of sounds. By default sounds will have spatialization
1346enabled, but if a sound does not need to be spatialized it's best to disable it. There are two ways
1347to disable spatialization of a sound:
1348
1349 ```c
1350 // Disable spatialization at initialization time via a flag:
1351 ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, NULL, &sound);
1352
1353 // Dynamically disable or enable spatialization post-initialization:
1354 ma_sound_set_spatialization_enabled(&sound, isSpatializationEnabled);
1355 ```
1356
1357By default sounds will be spatialized based on the closest listener. If a sound should always be
1358spatialized relative to a specific listener it can be pinned to one:
1359
1360 ```c
1361 ma_sound_set_pinned_listener_index(&sound, listenerIndex);
1362 ```
1363
1364Like listeners, sounds have a position. By default, the position of a sound is in absolute space,
1365but it can be changed to be relative to a listener:
1366
1367 ```c
1368 ma_sound_set_positioning(&sound, ma_positioning_relative);
1369 ```
1370
1371Note that relative positioning of a sound only makes sense if there is either only one listener, or
1372the sound is pinned to a specific listener. To set the position of a sound:
1373
1374 ```c
1375 ma_sound_set_position(&sound, posX, posY, posZ);
1376 ```
1377
1378The direction works the same way as a listener and represents the sound's forward direction:
1379
1380 ```c
1381 ma_sound_set_direction(&sound, forwardX, forwardY, forwardZ);
1382 ```
1383
1384Sound's also have a cone for controlling directional attenuation. This works exactly the same as
1385listeners:
1386
1387 ```c
1388 ma_sound_set_cone(&sound, innerAngleInRadians, outerAngleInRadians, outerGain);
1389 ```
1390
1391The velocity of a sound is used for doppler effect and can be set as such:
1392
1393 ```c
1394 ma_sound_set_velocity(&sound, velocityX, velocityY, velocityZ);
1395 ```
1396
1397The engine supports different attenuation models which can be configured on a per-sound basis. By
1398default the attenuation model is set to `ma_attenuation_model_inverse` which is the equivalent to
1399OpenAL's `AL_INVERSE_DISTANCE_CLAMPED`. Configure the attenuation model like so:
1400
1401 ```c
1402 ma_sound_set_attenuation_model(&sound, ma_attenuation_model_inverse);
1403 ```
1404
1405The supported attenuation models include the following:
1406
1407 +----------------------------------+----------------------------------------------+
1408 | ma_attenuation_model_none | No distance attenuation. |
1409 +----------------------------------+----------------------------------------------+
1410 | ma_attenuation_model_inverse | Equivalent to `AL_INVERSE_DISTANCE_CLAMPED`. |
1411 +----------------------------------+----------------------------------------------+
1412 | ma_attenuation_model_linear | Linear attenuation. |
1413 +----------------------------------+----------------------------------------------+
1414 | ma_attenuation_model_exponential | Exponential attenuation. |
1415 +----------------------------------+----------------------------------------------+
1416
1417To control how quickly a sound rolls off as it moves away from the listener, you need to configure
1418the rolloff:
1419
1420 ```c
1421 ma_sound_set_rolloff(&sound, rolloff);
1422 ```
1423
1424You can control the minimum and maximum gain to apply from spatialization:
1425
1426 ```c
1427 ma_sound_set_min_gain(&sound, minGain);
1428 ma_sound_set_max_gain(&sound, maxGain);
1429 ```
1430
1431Likewise, in the calculation of attenuation, you can control the minimum and maximum distances for
1432the attenuation calculation. This is useful if you want to ensure sounds don't drop below a certain
1433volume after the listener moves further away and to have sounds play a maximum volume when the
1434listener is within a certain distance:
1435
1436 ```c
1437 ma_sound_set_min_distance(&sound, minDistance);
1438 ma_sound_set_max_distance(&sound, maxDistance);
1439 ```
1440
1441The engine's spatialization system supports doppler effect. The doppler factor can be configure on
1442a per-sound basis like so:
1443
1444 ```c
1445 ma_sound_set_doppler_factor(&sound, dopplerFactor);
1446 ```
1447
1448You can fade sounds in and out with `ma_sound_set_fade_in_pcm_frames()` and
1449`ma_sound_set_fade_in_milliseconds()`. Set the volume to -1 to use the current volume as the
1450starting volume:
1451
1452 ```c
1453 // Fade in over 1 second.
1454 ma_sound_set_fade_in_milliseconds(&sound, 0, 1, 1000);
1455
1456 // ... sometime later ...
1457
1458 // Fade out over 1 second, starting from the current volume.
1459 ma_sound_set_fade_in_milliseconds(&sound, -1, 0, 1000);
1460 ```
1461
1462By default sounds will start immediately, but sometimes for timing and synchronization purposes it
1463can be useful to schedule a sound to start or stop:
1464
1465 ```c
1466 // Start the sound in 1 second from now.
1467 ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 1));
1468
1469 // Stop the sound in 2 seconds from now.
1470 ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2));
1471 ```
1472
1473Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before
1474anything will play.
1475
1476The time is specified in global time which is controlled by the engine. You can get the engine's
1477current time with `ma_engine_get_time_in_pcm_frames()`. The engine's global time is incremented
1478automatically as audio data is read, but it can be reset with `ma_engine_set_time_in_pcm_frames()`
1479in case it needs to be resynchronized for some reason.
1480
1481To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will
1482take the scheduled start and stop times into account.
1483
1484Whether or not a sound should loop can be controlled with `ma_sound_set_looping()`. Sounds will not
1485be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping.
1486
1487Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping
1488sound this should never return true. Alternatively, you can configure a callback that will be fired
1489when the sound reaches the end. Note that the callback is fired from the audio thread which means
1490you cannot be uninitializing sound from the callback. To set the callback you can use
1491`ma_sound_set_end_callback()`. Alternatively, if you're using `ma_sound_init_ex()`, you can pass it
1492into the config like so:
1493
1494 ```c
1495 soundConfig.endCallback = my_end_callback;
1496 soundConfig.pEndCallbackUserData = pMyEndCallbackUserData;
1497 ```
1498
1499The end callback is declared like so:
1500
1501 ```c
1502 void my_end_callback(void* pUserData, ma_sound* pSound)
1503 {
1504 ...
1505 }
1506 ```
1507
1508Internally a sound wraps around a data source. Some APIs exist to control the underlying data
1509source, mainly for convenience:
1510
1511 ```c
1512 ma_sound_seek_to_pcm_frame(&sound, frameIndex);
1513 ma_sound_get_data_format(&sound, &format, &channels, &sampleRate, pChannelMap, channelMapCapacity);
1514 ma_sound_get_cursor_in_pcm_frames(&sound, &cursor);
1515 ma_sound_get_length_in_pcm_frames(&sound, &length);
1516 ```
1517
1518Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do
1519not have any notion of a data source, anything relating to a data source is unavailable.
1520
1521Internally, sound data is loaded via the `ma_decoder` API which means by default it only supports
1522file formats that have built-in support in miniaudio. You can extend this to support any kind of
1523file format through the use of custom decoders. To do this you'll need to use a self-managed
1524resource manager and configure it appropriately. See the "Resource Management" section below for
1525details on how to set this up.
1526
1527
15286. Resource Management
1529======================
1530Many programs will want to manage sound resources for things such as reference counting and
1531streaming. This is supported by miniaudio via the `ma_resource_manager` API.
1532
1533The resource manager is mainly responsible for the following:
1534
1535 * Loading of sound files into memory with reference counting.
1536 * Streaming of sound data.
1537
1538When loading a sound file, the resource manager will give you back a `ma_data_source` compatible
1539object called `ma_resource_manager_data_source`. This object can be passed into any
1540`ma_data_source` API which is how you can read and seek audio data. When loading a sound file, you
1541specify whether or not you want the sound to be fully loaded into memory (and optionally
1542pre-decoded) or streamed. When loading into memory, you can also specify whether or not you want
1543the data to be loaded asynchronously.
1544
1545The example below is how you can initialize a resource manager using it's default configuration:
1546
1547 ```c
1548 ma_resource_manager_config config;
1549 ma_resource_manager resourceManager;
1550
1551 config = ma_resource_manager_config_init();
1552 result = ma_resource_manager_init(&config, &resourceManager);
1553 if (result != MA_SUCCESS) {
1554 ma_device_uninit(&device);
1555 printf("Failed to initialize the resource manager.");
1556 return -1;
1557 }
1558 ```
1559
1560You can configure the format, channels and sample rate of the decoded audio data. By default it
1561will use the file's native data format, but you can configure it to use a consistent format. This
1562is useful for offloading the cost of data conversion to load time rather than dynamically
1563converting at mixing time. To do this, you configure the decoded format, channels and sample rate
1564like the code below:
1565
1566 ```c
1567 config = ma_resource_manager_config_init();
1568 config.decodedFormat = device.playback.format;
1569 config.decodedChannels = device.playback.channels;
1570 config.decodedSampleRate = device.sampleRate;
1571 ```
1572
1573In the code above, the resource manager will be configured so that any decoded audio data will be
1574pre-converted at load time to the device's native data format. If instead you used defaults and
1575the data format of the file did not match the device's data format, you would need to convert the
1576data at mixing time which may be prohibitive in high-performance and large scale scenarios like
1577games.
1578
1579Internally the resource manager uses the `ma_decoder` API to load sounds. This means by default it
1580only supports decoders that are built into miniaudio. It's possible to support additional encoding
1581formats through the use of custom decoders. To do so, pass in your `ma_decoding_backend_vtable`
1582vtables into the resource manager config:
1583
1584 ```c
1585 ma_decoding_backend_vtable* pCustomBackendVTables[] =
1586 {
1587 &g_ma_decoding_backend_vtable_libvorbis,
1588 &g_ma_decoding_backend_vtable_libopus
1589 };
1590
1591 ...
1592
1593 resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables;
1594 resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);
1595 resourceManagerConfig.pCustomDecodingBackendUserData = NULL;
1596 ```
1597
1598This system can allow you to support any kind of file format. See the "Decoding" section for
1599details on how to implement custom decoders. The miniaudio repository includes examples for Opus
1600via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile.
1601
1602Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the
1603decoding of a page, a job will be posted to a queue which will then be processed by a job thread.
1604By default there will be only one job thread running, but this can be configured, like so:
1605
1606 ```c
1607 config = ma_resource_manager_config_init();
1608 config.jobThreadCount = MY_JOB_THREAD_COUNT;
1609 ```
1610
1611By default job threads are managed internally by the resource manager, however you can also self
1612manage your job threads if, for example, you want to integrate the job processing into your
1613existing job infrastructure, or if you simply don't like the way the resource manager does it. To
1614do this, just set the job thread count to 0 and process jobs manually. To process jobs, you first
1615need to retrieve a job using `ma_resource_manager_next_job()` and then process it using
1616`ma_job_process()`:
1617
1618 ```c
1619 config = ma_resource_manager_config_init();
1620 config.jobThreadCount = 0; // Don't manage any job threads internally.
1621 config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; // Optional. Makes `ma_resource_manager_next_job()` non-blocking.
1622
1623 // ... Initialize your custom job threads ...
1624
1625 void my_custom_job_thread(...)
1626 {
1627 for (;;) {
1628 ma_job job;
1629 ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job);
1630 if (result != MA_SUCCESS) {
1631 if (result == MA_NO_DATA_AVAILABLE) {
1632 // No jobs are available. Keep going. Will only get this if the resource manager was initialized
1633 // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING.
1634 continue;
1635 } else if (result == MA_CANCELLED) {
1636 // MA_JOB_TYPE_QUIT was posted. Exit.
1637 break;
1638 } else {
1639 // Some other error occurred.
1640 break;
1641 }
1642 }
1643
1644 ma_job_process(&job);
1645 }
1646 }
1647 ```
1648
1649In the example above, the `MA_JOB_TYPE_QUIT` event is the used as the termination
1650indicator, but you can use whatever you would like to terminate the thread. The call to
1651`ma_resource_manager_next_job()` is blocking by default, but can be configured to be non-blocking
1652by initializing the resource manager with the `MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING` configuration
1653flag. Note that the `MA_JOB_TYPE_QUIT` will never be removed from the job queue. This
1654is to give every thread the opportunity to catch the event and terminate naturally.
1655
1656When loading a file, it's sometimes convenient to be able to customize how files are opened and
1657read instead of using standard `fopen()`, `fclose()`, etc. which is what miniaudio will use by
1658default. This can be done by setting `pVFS` member of the resource manager's config:
1659
1660 ```c
1661 // Initialize your custom VFS object. See documentation for VFS for information on how to do this.
1662 my_custom_vfs vfs = my_custom_vfs_init();
1663
1664 config = ma_resource_manager_config_init();
1665 config.pVFS = &vfs;
1666 ```
1667
1668This is particularly useful in programs like games where you want to read straight from an archive
1669rather than the normal file system. If you do not specify a custom VFS, the resource manager will
1670use the operating system's normal file operations.
1671
1672To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When
1673loading a sound you need to specify the file path and options for how the sounds should be loaded.
1674By default a sound will be loaded synchronously. The returned data source is owned by the caller
1675which means the caller is responsible for the allocation and freeing of the data source. Below is
1676an example for initializing a data source:
1677
1678 ```c
1679 ma_resource_manager_data_source dataSource;
1680 ma_result result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource);
1681 if (result != MA_SUCCESS) {
1682 // Error.
1683 }
1684
1685 // ...
1686
1687 // A ma_resource_manager_data_source object is compatible with the `ma_data_source` API. To read data, just call
1688 // the `ma_data_source_read_pcm_frames()` like you would with any normal data source.
1689 result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead);
1690 if (result != MA_SUCCESS) {
1691 // Failed to read PCM frames.
1692 }
1693
1694 // ...
1695
1696 ma_resource_manager_data_source_uninit(&dataSource);
1697 ```
1698
1699The `flags` parameter specifies how you want to perform loading of the sound file. It can be a
1700combination of the following flags:
1701
1702 ```
1703 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM
1704 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE
1705 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC
1706 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT
1707 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING
1708 ```
1709
1710When no flags are specified (set to 0), the sound will be fully loaded into memory, but not
1711decoded, meaning the raw file data will be stored in memory, and then dynamically decoded when
1712`ma_data_source_read_pcm_frames()` is called. To instead decode the audio data before storing it in
1713memory, use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` flag. By default, the sound file will
1714be loaded synchronously, meaning `ma_resource_manager_data_source_init()` will only return after
1715the entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You
1716can instead load the sound asynchronously using the `MA_RESOURCE_MANAGER_DATA_SOURCE_ASYNC` flag.
1717This will result in `ma_resource_manager_data_source_init()` returning quickly, but no data will be
1718returned by `ma_data_source_read_pcm_frames()` until some data is available. When no data is
1719available because the asynchronous decoding hasn't caught up, `MA_BUSY` will be returned by
1720`ma_data_source_read_pcm_frames()`.
1721
1722For large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you
1723can instead stream audio data which you can do by specifying the
1724`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. When streaming, data will be decoded in 1
1725second pages. When a new page needs to be decoded, a job will be posted to the job queue and then
1726subsequently processed in a job thread.
1727
1728The `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag can be used so that the sound will loop
1729when it reaches the end by default. It's recommended you use this flag when you want to have a
1730looping streaming sound. If you try loading a very short sound as a stream, you will get a glitch.
1731This is because the resource manager needs to pre-fill the initial buffer at initialization time,
1732and if you don't specify the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flag, the resource
1733manager will assume the sound is not looping and will stop filling the buffer when it reaches the
1734end, therefore resulting in a discontinuous buffer.
1735
1736For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means
1737multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in
1738the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be
1739matched up with a call to `ma_resource_manager_data_source_uninit()`. Sometimes it can be useful
1740for a program to register self-managed raw audio data and associate it with a file path. Use the
1741`ma_resource_manager_register_*()` and `ma_resource_manager_unregister_*()` APIs to do this.
1742`ma_resource_manager_register_decoded_data()` is used to associate a pointer to raw, self-managed
1743decoded audio data in the specified data format with the specified name. Likewise,
1744`ma_resource_manager_register_encoded_data()` is used to associate a pointer to raw self-managed
1745encoded audio data (the raw file data) with the specified name. Note that these names need not be
1746actual file paths. When `ma_resource_manager_data_source_init()` is called (without the
1747`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these
1748explicitly registered data buffers and, if found, will use it as the backing data for the data
1749source. Note that the resource manager does *not* make a copy of this data so it is up to the
1750caller to ensure the pointer stays valid for its lifetime. Use
1751`ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use
1752`ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and
1753unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM`
1754flag with a self-managed data pointer.
1755
1756
17576.1. Asynchronous Loading and Synchronization
1758---------------------------------------------
1759When loading asynchronously, it can be useful to poll whether or not loading has finished. Use
1760`ma_resource_manager_data_source_result()` to determine this. For in-memory sounds, this will
1761return `MA_SUCCESS` when the file has been *entirely* decoded. If the sound is still being decoded,
1762`MA_BUSY` will be returned. Otherwise, some other error code will be returned if the sound failed
1763to load. For streaming data sources, `MA_SUCCESS` will be returned when the first page has been
1764decoded and the sound is ready to be played. If the first page is still being decoded, `MA_BUSY`
1765will be returned. Otherwise, some other error code will be returned if the sound failed to load.
1766
1767In addition to polling, you can also use a simple synchronization object called a "fence" to wait
1768for asynchronously loaded sounds to finish. This is called `ma_fence`. The advantage to using a
1769fence is that it can be used to wait for a group of sounds to finish loading rather than waiting
1770for sounds on an individual basis. There are two stages to loading a sound:
1771
1772 * Initialization of the internal decoder; and
1773 * Completion of decoding of the file (the file is fully decoded)
1774
1775You can specify separate fences for each of the different stages. Waiting for the initialization
1776of the internal decoder is important for when you need to know the sample format, channels and
1777sample rate of the file.
1778
1779The example below shows how you could use a fence when loading a number of sounds:
1780
1781 ```c
1782 // This fence will be released when all sounds are finished loading entirely.
1783 ma_fence fence;
1784 ma_fence_init(&fence);
1785
1786 // This will be passed into the initialization routine for each sound.
1787 ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init();
1788 notifications.done.pFence = &fence;
1789
1790 // Now load a bunch of sounds:
1791 for (iSound = 0; iSound < soundCount; iSound += 1) {
1792 ma_resource_manager_data_source_init(pResourceManager, pSoundFilePaths[iSound], flags, &notifications, &pSoundSources[iSound]);
1793 }
1794
1795 // ... DO SOMETHING ELSE WHILE SOUNDS ARE LOADING ...
1796
1797 // Wait for loading of sounds to finish.
1798 ma_fence_wait(&fence);
1799 ```
1800
1801In the example above we used a fence for waiting until the entire file has been fully decoded. If
1802you only need to wait for the initialization of the internal decoder to complete, you can use the
1803`init` member of the `ma_resource_manager_pipeline_notifications` object:
1804
1805 ```c
1806 notifications.init.pFence = &fence;
1807 ```
1808
1809If a fence is not appropriate for your situation, you can instead use a callback that is fired on
1810an individual sound basis. This is done in a very similar way to fences:
1811
1812 ```c
1813 typedef struct
1814 {
1815 ma_async_notification_callbacks cb;
1816 void* pMyData;
1817 } my_notification;
1818
1819 void my_notification_callback(ma_async_notification* pNotification)
1820 {
1821 my_notification* pMyNotification = (my_notification*)pNotification;
1822
1823 // Do something in response to the sound finishing loading.
1824 }
1825
1826 ...
1827
1828 my_notification myCallback;
1829 myCallback.cb.onSignal = my_notification_callback;
1830 myCallback.pMyData = pMyData;
1831
1832 ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init();
1833 notifications.done.pNotification = &myCallback;
1834
1835 ma_resource_manager_data_source_init(pResourceManager, "my_sound.wav", flags, &notifications, &mySound);
1836 ```
1837
1838In the example above we just extend the `ma_async_notification_callbacks` object and pass an
1839instantiation into the `ma_resource_manager_pipeline_notifications` in the same way as we did with
1840the fence, only we set `pNotification` instead of `pFence`. You can set both of these at the same
1841time and they should both work as expected. If using the `pNotification` system, you need to ensure
1842your `ma_async_notification_callbacks` object stays valid.
1843
1844
1845
18466.2. Resource Manager Implementation Details
1847--------------------------------------------
1848Resources are managed in two main ways:
1849
1850 * By storing the entire sound inside an in-memory buffer (referred to as a data buffer)
1851 * By streaming audio data on the fly (referred to as a data stream)
1852
1853A resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or
1854data stream, depending on whether or not the data source was initialized with the
1855`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a
1856`ma_resource_manager_data_stream` object. Otherwise it will use a `ma_resource_manager_data_buffer`
1857object. Both of these objects are data sources which means they can be used with any
1858`ma_data_source_*()` API.
1859
1860Another major feature of the resource manager is the ability to asynchronously decode audio files.
1861This relieves the audio thread of time-consuming decoding which can negatively affect scalability
1862due to the audio thread needing to complete it's work extremely quickly to avoid glitching.
1863Asynchronous decoding is achieved through a job system. There is a central multi-producer,
1864multi-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is
1865posted to the queue which is then read by a job thread. The number of job threads can be
1866configured for improved scalability, and job threads can all run in parallel without needing to
1867worry about the order of execution (how this is achieved is explained below).
1868
1869When a sound is being loaded asynchronously, playback can begin before the sound has been fully
1870decoded. This enables the application to start playback of the sound quickly, while at the same
1871time allowing to resource manager to keep loading in the background. Since there may be less
1872threads than the number of sounds being loaded at a given time, a simple scheduling system is used
1873to keep decoding time balanced and fair. The resource manager solves this by splitting decoding
1874into chunks called pages. By default, each page is 1 second long. When a page has been decoded, a
1875new job will be posted to start decoding the next page. By dividing up decoding into pages, an
1876individual sound shouldn't ever delay every other sound from having their first page decoded. Of
1877course, when loading many sounds at the same time, there will always be an amount of time required
1878to process jobs in the queue so in heavy load situations there will still be some delay. To
1879determine if a data source is ready to have some frames read, use
1880`ma_resource_manager_data_source_get_available_frames()`. This will return the number of frames
1881available starting from the current position.
1882
1883
18846.2.1. Job Queue
1885----------------
1886The resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity.
1887This job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety.
1888Only a fixed number of jobs can be allocated and inserted into the queue which is done through a
1889lock-free data structure for allocating an index into a fixed sized array, with reference counting
1890for mitigation of the ABA problem. The reference count is 32-bit.
1891
1892For many types of jobs it's important that they execute in a specific order. In these cases, jobs
1893are executed serially. For the resource manager, serial execution of jobs is only required on a
1894per-object basis (per data buffer or per data stream). Each of these objects stores an execution
1895counter. When a job is posted it is associated with an execution counter. When the job is
1896processed, it checks if the execution counter of the job equals the execution counter of the
1897owning object and if so, processes the job. If the counters are not equal, the job will be posted
1898back onto the job queue for later processing. When the job finishes processing the execution order
1899of the main object is incremented. This system means the no matter how many job threads are
1900executing, decoding of an individual sound will always get processed serially. The advantage to
1901having multiple threads comes into play when loading multiple sounds at the same time.
1902
1903The resource manager's job queue is not 100% lock-free and will use a spinlock to achieve
1904thread-safety for a very small section of code. This is only relevant when the resource manager
1905uses more than one job thread. If only using a single job thread, which is the default, the
1906lock should never actually wait in practice. The amount of time spent locking should be quite
1907short, but it's something to be aware of for those who have pedantic lock-free requirements and
1908need to use more than one job thread. There are plans to remove this lock in a future version.
1909
1910In addition, posting a job will release a semaphore, which on Win32 is implemented with
1911`ReleaseSemaphore` and on POSIX platforms via a condition variable:
1912
1913 ```c
1914 pthread_mutex_lock(&pSemaphore->lock);
1915 {
1916 pSemaphore->value += 1;
1917 pthread_cond_signal(&pSemaphore->cond);
1918 }
1919 pthread_mutex_unlock(&pSemaphore->lock);
1920 ```
1921
1922Again, this is relevant for those with strict lock-free requirements in the audio thread. To avoid
1923this, you can use non-blocking mode (via the `MA_JOB_QUEUE_FLAG_NON_BLOCKING`
1924flag) and implement your own job processing routine (see the "Resource Manager" section above for
1925details on how to do this).
1926
1927
1928
19296.2.2. Data Buffers
1930-------------------
1931When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag is excluded at initialization time, the
1932resource manager will try to load the data into an in-memory data buffer. Before doing so, however,
1933it will first check if the specified file is already loaded. If so, it will increment a reference
1934counter and just use the already loaded data. This saves both time and memory. When the data buffer
1935is uninitialized, the reference counter will be decremented. If the counter hits zero, the file
1936will be unloaded. This is a detail to keep in mind because it could result in excessive loading and
1937unloading of a sound. For example, the following sequence will result in a file be loaded twice,
1938once after the other:
1939
1940 ```c
1941 ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load.
1942 ma_resource_manager_data_source_uninit(&myDataBuffer0); // Refcount = 0. Unloaded.
1943
1944 ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it.
1945 ma_resource_manager_data_source_uninit(&myDataBuffer1); // Refcount = 0. Unloaded.
1946 ```
1947
1948A binary search tree (BST) is used for storing data buffers as it has good balance between
1949efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed
1950into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves
1951memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST
1952due to the random nature of the hash. The disadvantages are that file names are case-sensitive and
1953there's a small chance of name collisions. If case-sensitivity is an issue, you should normalize
1954your file names to upper- or lower-case before initializing your data sources. If name collisions
1955become an issue, you'll need to change the name of one of the colliding names or just not use the
1956resource manager.
1957
1958When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC`
1959flag is excluded, the file will be decoded synchronously by the calling thread. There are two
1960options for controlling how the audio is stored in the data buffer - encoded or decoded. When the
1961`MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` option is excluded, the raw file data will be stored
1962in memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is
1963a very simple and standard process of simply adding an item to the BST, allocating a block of
1964memory and then decoding (if `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` is specified).
1965
1966When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is specified, loading of the data buffer
1967is done asynchronously. In this case, a job is posted to the queue to start loading and then the
1968function immediately returns, setting an internal result code to `MA_BUSY`. This result code is
1969returned when the program calls `ma_resource_manager_data_source_result()`. When decoding has fully
1970completed `MA_SUCCESS` will be returned. This can be used to know if loading has fully completed.
1971
1972When loading asynchronously, a single job is posted to the queue of the type
1973`MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE`. This involves making a copy of the file path and
1974associating it with job. When the job is processed by the job thread, it will first load the file
1975using the VFS associated with the resource manager. When using a custom VFS, it's important that it
1976be completely thread-safe because it will be used from one or more job threads at the same time.
1977Individual files should only ever be accessed by one thread at a time, however. After opening the
1978file via the VFS, the job will determine whether or not the file is being decoded. If not, it
1979simply allocates a block of memory and loads the raw file contents into it and returns. On the
1980other hand, when the file is being decoded, it will first allocate a decoder on the heap and
1981initialize it. Then it will check if the length of the file is known. If so it will allocate a
1982block of memory to store the decoded output and initialize it to silence. If the size is unknown,
1983it will allocate room for one page. After memory has been allocated, the first page will be
1984decoded. If the sound is shorter than a page, the result code will be set to `MA_SUCCESS` and the
1985completion event will be signalled and loading is now complete. If, however, there is more to
1986decode, a job with the code `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` is posted. This job
1987will decode the next page and perform the same process if it reaches the end. If there is more to
1988decode, the job will post another `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` job which will
1989keep on happening until the sound has been fully decoded. For sounds of an unknown length, each
1990page will be linked together as a linked list. Internally this is implemented via the
1991`ma_paged_audio_buffer` object.
1992
1993
19946.2.3. Data Streams
1995-------------------
1996Data streams only ever store two pages worth of data for each instance. They are most useful for
1997large sounds like music tracks in games that would consume too much memory if fully decoded in
1998memory. After every frame from a page has been read, a job will be posted to load the next page
1999which is done from the VFS.
2000
2001For data streams, the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag will determine whether or
2002not initialization of the data source waits until the two pages have been decoded. When unset,
2003`ma_resource_manager_data_source_init()` will wait until the two pages have been loaded, otherwise
2004it will return immediately.
2005
2006When frames are read from a data stream using `ma_resource_manager_data_source_read_pcm_frames()`,
2007`MA_BUSY` will be returned if there are no frames available. If there are some frames available,
2008but less than the number requested, `MA_SUCCESS` will be returned, but the actual number of frames
2009read will be less than the number requested. Due to the asynchronous nature of data streams,
2010seeking is also asynchronous. If the data stream is in the middle of a seek, `MA_BUSY` will be
2011returned when trying to read frames.
2012
2013When `ma_resource_manager_data_source_read_pcm_frames()` results in a page getting fully consumed
2014a job is posted to load the next page. This will be posted from the same thread that called
2015`ma_resource_manager_data_source_read_pcm_frames()`.
2016
2017Data streams are uninitialized by posting a job to the queue, but the function won't return until
2018that job has been processed. The reason for this is that the caller owns the data stream object and
2019therefore miniaudio needs to ensure everything completes before handing back control to the caller.
2020Also, if the data stream is uninitialized while pages are in the middle of decoding, they must
2021complete before destroying any underlying object and the job system handles this cleanly.
2022
2023Note that when a new page needs to be loaded, a job will be posted to the resource manager's job
2024thread from the audio thread. You must keep in mind the details mentioned in the "Job Queue"
2025section above regarding locking when posting an event if you require a strictly lock-free audio
2026thread.
2027
2028
2029
20307. Node Graph
2031=============
2032miniaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a
2033node whose outputs are attached to inputs of another node, thereby creating a graph. There are
2034different types of nodes, with each node in the graph processing input data to produce output,
2035which is then fed through the chain. Each node in the graph can apply their own custom effects. At
2036the start of the graph will usually be one or more data source nodes which have no inputs and
2037instead pull their data from a data source. At the end of the graph is an endpoint which represents
2038the end of the chain and is where the final output is ultimately extracted from.
2039
2040Each node has a number of input buses and a number of output buses. An output bus from a node is
2041attached to an input bus of another. Multiple nodes can connect their output buses to another
2042node's input bus, in which case their outputs will be mixed before processing by the node. Below is
2043a diagram that illustrates a hypothetical node graph setup:
2044
2045 ```
2046 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
2047
2048 +---------------+ +-----------------+
2049 | Data Source 1 =----+ +----------+ +----= Low Pass Filter =----+
2050 +---------------+ | | =----+ +-----------------+ | +----------+
2051 +----= Splitter | +----= ENDPOINT |
2052 +---------------+ | | =----+ +-----------------+ | +----------+
2053 | Data Source 2 =----+ +----------+ +----= Echo / Delay =----+
2054 +---------------+ +-----------------+
2055 ```
2056
2057In the above graph, it starts with two data sources whose outputs are attached to the input of a
2058splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter
2059performs it's processing routine and produces two outputs which is simply a duplication of the
2060input stream. One output is attached to a low pass filter, whereas the other output is attached to
2061a echo/delay. The outputs of the low pass filter and the echo are attached to the endpoint, and
2062since they're both connected to the same input bus, they'll be mixed.
2063
2064Each input bus must be configured to accept the same number of channels, but the number of channels
2065used by input buses can be different to the number of channels for output buses in which case
2066miniaudio will automatically convert the input data to the output channel count before processing.
2067The number of channels of an output bus of one node must match the channel count of the input bus
2068it's attached to. The channel counts cannot be changed after the node has been initialized. If you
2069attempt to attach an output bus to an input bus with a different channel count, attachment will
2070fail.
2071
2072To use a node graph, you first need to initialize a `ma_node_graph` object. This is essentially a
2073container around the entire graph. The `ma_node_graph` object is required for some thread-safety
2074issues which will be explained later. A `ma_node_graph` object is initialized using miniaudio's
2075standard config/init system:
2076
2077 ```c
2078 ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount);
2079
2080 result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph); // Second parameter is a pointer to allocation callbacks.
2081 if (result != MA_SUCCESS) {
2082 // Failed to initialize node graph.
2083 }
2084 ```
2085
2086When you initialize the node graph, you're specifying the channel count of the endpoint. The
2087endpoint is a special node which has one input bus and one output bus, both of which have the
2088same channel count, which is specified in the config. Any nodes that connect directly to the
2089endpoint must be configured such that their output buses have the same channel count. When you read
2090audio data from the node graph, it'll have the channel count you specified in the config. To read
2091data from the graph:
2092
2093 ```c
2094 ma_uint32 framesRead;
2095 result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead);
2096 if (result != MA_SUCCESS) {
2097 // Failed to read data from the node graph.
2098 }
2099 ```
2100
2101When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in
2102data from its input attachments, which in turn recursively pull in data from their inputs, and so
2103on. At the start of the graph there will be some kind of data source node which will have zero
2104inputs and will instead read directly from a data source. The base nodes don't literally need to
2105read from a `ma_data_source` object, but they will always have some kind of underlying object that
2106sources some kind of audio. The `ma_data_source_node` node can be used to read from a
2107`ma_data_source`. Data is always in floating-point format and in the number of channels you
2108specified when the graph was initialized. The sample rate is defined by the underlying data sources.
2109It's up to you to ensure they use a consistent and appropriate sample rate.
2110
2111The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but
2112miniaudio includes a few stock nodes for common functionality. This is how you would initialize a
2113node which reads directly from a data source (`ma_data_source_node`) which is an example of one
2114of the stock nodes that comes with miniaudio:
2115
2116 ```c
2117 ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource);
2118
2119 ma_data_source_node dataSourceNode;
2120 result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode);
2121 if (result != MA_SUCCESS) {
2122 // Failed to create data source node.
2123 }
2124 ```
2125
2126The data source node will use the output channel count to determine the channel count of the output
2127bus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data
2128source). The data source must output to floating-point (`ma_format_f32`) or else an error will be
2129returned from `ma_data_source_node_init()`.
2130
2131By default the node will not be attached to the graph. To do so, use `ma_node_attach_output_bus()`:
2132
2133 ```c
2134 result = ma_node_attach_output_bus(&dataSourceNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0);
2135 if (result != MA_SUCCESS) {
2136 // Failed to attach node.
2137 }
2138 ```
2139
2140The code above connects the data source node directly to the endpoint. Since the data source node
2141has only a single output bus, the index will always be 0. Likewise, the endpoint only has a single
2142input bus which means the input bus index will also always be 0.
2143
2144To detach a specific output bus, use `ma_node_detach_output_bus()`. To detach all output buses, use
2145`ma_node_detach_all_output_buses()`. If you want to just move the output bus from one attachment to
2146another, you do not need to detach first. You can just call `ma_node_attach_output_bus()` and it'll
2147deal with it for you.
2148
2149Less frequently you may want to create a specialized node. This will be a node where you implement
2150your own processing callback to apply a custom effect of some kind. This is similar to initializing
2151one of the stock node types, only this time you need to specify a pointer to a vtable containing a
2152pointer to the processing function and the number of input and output buses. Example:
2153
2154 ```c
2155 static void my_custom_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
2156 {
2157 // Do some processing of ppFramesIn (one stream of audio data per input bus)
2158 const float* pFramesIn_0 = ppFramesIn[0]; // Input bus @ index 0.
2159 const float* pFramesIn_1 = ppFramesIn[1]; // Input bus @ index 1.
2160 float* pFramesOut_0 = ppFramesOut[0]; // Output bus @ index 0.
2161
2162 // Do some processing. On input, `pFrameCountIn` will be the number of input frames in each
2163 // buffer in `ppFramesIn` and `pFrameCountOut` will be the capacity of each of the buffers
2164 // in `ppFramesOut`. On output, `pFrameCountIn` should be set to the number of input frames
2165 // your node consumed and `pFrameCountOut` should be set the number of output frames that
2166 // were produced.
2167 //
2168 // You should process as many frames as you can. If your effect consumes input frames at the
2169 // same rate as output frames (always the case, unless you're doing resampling), you need
2170 // only look at `ppFramesOut` and process that exact number of frames. If you're doing
2171 // resampling, you'll need to be sure to set both `pFrameCountIn` and `pFrameCountOut`
2172 // properly.
2173 }
2174
2175 static ma_node_vtable my_custom_node_vtable =
2176 {
2177 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.
2178 NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames.
2179 2, // 2 input buses.
2180 1, // 1 output bus.
2181 0 // Default flags.
2182 };
2183
2184 ...
2185
2186 // Each bus needs to have a channel count specified. To do this you need to specify the channel
2187 // counts in an array and then pass that into the node config.
2188 ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable.
2189 ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specified in the vtable.
2190
2191 inputChannels[0] = channelsIn;
2192 inputChannels[1] = channelsIn;
2193 outputChannels[0] = channelsOut;
2194
2195 ma_node_config nodeConfig = ma_node_config_init();
2196 nodeConfig.vtable = &my_custom_node_vtable;
2197 nodeConfig.pInputChannels = inputChannels;
2198 nodeConfig.pOutputChannels = outputChannels;
2199
2200 ma_node_base node;
2201 result = ma_node_init(&nodeGraph, &nodeConfig, NULL, &node);
2202 if (result != MA_SUCCESS) {
2203 // Failed to initialize node.
2204 }
2205 ```
2206
2207When initializing a custom node, as in the code above, you'll normally just place your vtable in
2208static space. The number of input and output buses are specified as part of the vtable. If you need
2209a variable number of buses on a per-node bases, the vtable should have the relevant bus count set
2210to `MA_NODE_BUS_COUNT_UNKNOWN`. In this case, the bus count should be set in the node config:
2211
2212 ```c
2213 static ma_node_vtable my_custom_node_vtable =
2214 {
2215 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.
2216 NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames.
2217 MA_NODE_BUS_COUNT_UNKNOWN, // The number of input buses is determined on a per-node basis.
2218 1, // 1 output bus.
2219 0 // Default flags.
2220 };
2221
2222 ...
2223
2224 ma_node_config nodeConfig = ma_node_config_init();
2225 nodeConfig.vtable = &my_custom_node_vtable;
2226 nodeConfig.inputBusCount = myBusCount; // <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here.
2227 nodeConfig.pInputChannels = inputChannels; // <-- Make sure there are nodeConfig.inputBusCount elements in this array.
2228 nodeConfig.pOutputChannels = outputChannels; // <-- The vtable specifies 1 output bus, so there must be 1 element in this array.
2229 ```
2230
2231In the above example it's important to never set the `inputBusCount` and `outputBusCount` members
2232to anything other than their defaults if the vtable specifies an explicit count. They can only be
2233set if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count.
2234
2235Most often you'll want to create a structure to encapsulate your node with some extra data. You
2236need to make sure the `ma_node_base` object is your first member of the structure:
2237
2238 ```c
2239 typedef struct
2240 {
2241 ma_node_base base; // <-- Make sure this is always the first member.
2242 float someCustomData;
2243 } my_custom_node;
2244 ```
2245
2246By doing this, your object will be compatible with all `ma_node` APIs and you can attach it to the
2247graph just like any other node.
2248
2249In the custom processing callback (`my_custom_node_process_pcm_frames()` in the example above), the
2250number of channels for each bus is what was specified by the config when the node was initialized
2251with `ma_node_init()`. In addition, all attachments to each of the input buses will have been
2252pre-mixed by miniaudio. The config allows you to specify different channel counts for each
2253individual input and output bus. It's up to the effect to handle it appropriate, and if it can't,
2254return an error in it's initialization routine.
2255
2256Custom nodes can be assigned some flags to describe their behaviour. These are set via the vtable
2257and include the following:
2258
2259 +-----------------------------------------+---------------------------------------------------+
2260 | Flag Name | Description |
2261 +-----------------------------------------+---------------------------------------------------+
2262 | MA_NODE_FLAG_PASSTHROUGH | Useful for nodes that do not do any kind of audio |
2263 | | processing, but are instead used for tracking |
2264 | | time, handling events, etc. Also used by the |
2265 | | internal endpoint node. It reads directly from |
2266 | | the input bus to the output bus. Nodes with this |
2267 | | flag must have exactly 1 input bus and 1 output |
2268 | | bus, and both buses must have the same channel |
2269 | | counts. |
2270 +-----------------------------------------+---------------------------------------------------+
2271 | MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even |
2272 | | when no data is available to be read from input |
2273 | | attachments. When a node has at least one input |
2274 | | bus, but there are no inputs attached or the |
2275 | | inputs do not deliver any data, the node's |
2276 | | processing callback will not get fired. This flag |
2277 | | will make it so the callback is always fired |
2278 | | regardless of whether or not any input data is |
2279 | | received. This is useful for effects like |
2280 | | echos where there will be a tail of audio data |
2281 | | that still needs to be processed even when the |
2282 | | original data sources have reached their ends. It |
2283 | | may also be useful for nodes that must always |
2284 | | have their processing callback fired when there |
2285 | | are no inputs attached. |
2286 +-----------------------------------------+---------------------------------------------------+
2287 | MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with |
2288 | | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this |
2289 | | is set, the `ppFramesIn` parameter of the |
2290 | | processing callback will be set to NULL when |
2291 | | there are no input frames are available. When |
2292 | | this is unset, silence will be posted to the |
2293 | | processing callback. |
2294 +-----------------------------------------+---------------------------------------------------+
2295 | MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES | Used to tell miniaudio that input and output |
2296 | | frames are processed at different rates. You |
2297 | | should set this for any nodes that perform |
2298 | | resampling. |
2299 +-----------------------------------------+---------------------------------------------------+
2300 | MA_NODE_FLAG_SILENT_OUTPUT | Used to tell miniaudio that a node produces only |
2301 | | silent output. This is useful for nodes where you |
2302 | | don't want the output to contribute to the final |
2303 | | mix. An example might be if you want split your |
2304 | | stream and have one branch be output to a file. |
2305 | | When using this flag, you should avoid writing to |
2306 | | the output buffer of the node's processing |
2307 | | callback because miniaudio will ignore it anyway. |
2308 +-----------------------------------------+---------------------------------------------------+
2309
2310
2311If you need to make a copy of an audio stream for effect processing you can use a splitter node
2312called `ma_splitter_node`. This takes has 1 input bus and splits the stream into 2 output buses.
2313You can use it like this:
2314
2315 ```c
2316 ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channels);
2317
2318 ma_splitter_node splitterNode;
2319 result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode);
2320 if (result != MA_SUCCESS) {
2321 // Failed to create node.
2322 }
2323
2324 // Attach your output buses to two different input buses (can be on two different nodes).
2325 ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint.
2326 ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); // Attach to input bus 0 of some effect node.
2327 ```
2328
2329The volume of an output bus can be configured on a per-bus basis:
2330
2331 ```c
2332 ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f);
2333 ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f);
2334 ```
2335
2336In the code above we're using the splitter node from before and changing the volume of each of the
2337copied streams.
2338
2339You can start and stop a node with the following:
2340
2341 ```c
2342 ma_node_set_state(&splitterNode, ma_node_state_started); // The default state.
2343 ma_node_set_state(&splitterNode, ma_node_state_stopped);
2344 ```
2345
2346By default the node is in a started state, but since it won't be connected to anything won't
2347actually be invoked by the node graph until it's connected. When you stop a node, data will not be
2348read from any of its input connections. You can use this property to stop a group of sounds
2349atomically.
2350
2351You can configure the initial state of a node in it's config:
2352
2353 ```c
2354 nodeConfig.initialState = ma_node_state_stopped;
2355 ```
2356
2357Note that for the stock specialized nodes, all of their configs will have a `nodeConfig` member
2358which is the config to use with the base node. This is where the initial state can be configured
2359for specialized nodes:
2360
2361 ```c
2362 dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped;
2363 ```
2364
2365When using a specialized node like `ma_data_source_node` or `ma_splitter_node`, be sure to not
2366modify the `vtable` member of the `nodeConfig` object.
2367
2368
23697.1. Timing
2370-----------
2371The node graph supports starting and stopping nodes at scheduled times. This is especially useful
2372for data source nodes where you want to get the node set up, but only start playback at a specific
2373time. There are two clocks: local and global.
2374
2375A local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can
2376only be done based on the global clock because the local clock will not be running while the node
2377is stopped. The global clocks advances whenever `ma_node_graph_read_pcm_frames()` is called. On the
2378other hand, the local clock only advances when the node's processing callback is fired, and is
2379advanced based on the output frame count.
2380
2381To retrieve the global time, use `ma_node_graph_get_time()`. The global time can be set with
2382`ma_node_graph_set_time()` which might be useful if you want to do seeking on a global timeline.
2383Getting and setting the local time is similar. Use `ma_node_get_time()` to retrieve the local time,
2384and `ma_node_set_time()` to set the local time. The global and local times will be advanced by the
2385audio thread, so care should be taken to avoid data races. Ideally you should avoid calling these
2386outside of the node processing callbacks which are always run on the audio thread.
2387
2388There is basic support for scheduling the starting and stopping of nodes. You can only schedule one
2389start and one stop at a time. This is mainly intended for putting nodes into a started or stopped
2390state in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited
2391to the resolution of a call to `ma_node_graph_read_pcm_frames()` which would typically be in blocks
2392of several milliseconds. The following APIs can be used for scheduling node states:
2393
2394 ```c
2395 ma_node_set_state_time()
2396 ma_node_get_state_time()
2397 ```
2398
2399The time is absolute and must be based on the global clock. An example is below:
2400
2401 ```c
2402 ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1); // Delay starting to 1 second.
2403 ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5); // Delay stopping to 5 seconds.
2404 ```
2405
2406An example for changing the state using a relative time.
2407
2408 ```c
2409 ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph));
2410 ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph));
2411 ```
2412
2413Note that due to the nature of multi-threading the times may not be 100% exact. If this is an
2414issue, consider scheduling state changes from within a processing callback. An idea might be to
2415have some kind of passthrough trigger node that is used specifically for tracking time and handling
2416events.
2417
2418
2419
24207.2. Thread Safety and Locking
2421------------------------------
2422When processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's
2423expected that `ma_node_graph_read_pcm_frames()` would be run on the audio thread, it does so
2424without the use of any locks. This section discusses the implementation used by miniaudio and goes
2425over some of the compromises employed by miniaudio to achieve this goal. Note that the current
2426implementation may not be ideal - feedback and critiques are most welcome.
2427
2428The node graph API is not *entirely* lock-free. Only `ma_node_graph_read_pcm_frames()` is expected
2429to be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the
2430implementation, but are crafted in a way such that such locking is not required when reading audio
2431data from the graph. Locking in these areas are achieved by means of spinlocks.
2432
2433The main complication with keeping `ma_node_graph_read_pcm_frames()` lock-free stems from the fact
2434that a node can be uninitialized, and it's memory potentially freed, while in the middle of being
2435processed on the audio thread. There are times when the audio thread will be referencing a node,
2436which means the uninitialization process of a node needs to make sure it delays returning until the
2437audio thread is finished so that control is not handed back to the caller thereby giving them a
2438chance to free the node's memory.
2439
2440When the audio thread is processing a node, it does so by reading from each of the output buses of
2441the node. In order for a node to process data for one of its output buses, it needs to read from
2442each of its input buses, and so on an so forth. It follows that once all output buses of a node
2443are detached, the node as a whole will be disconnected and no further processing will occur unless
2444it's output buses are reattached, which won't be happening when the node is being uninitialized.
2445By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can
2446simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By
2447doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output
2448nodes, followed by each of the attachments to each of its input nodes, and then do any final clean
2449up.
2450
2451With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as
2452it takes to process the output bus being detached. This will happen if it's called at just the
2453wrong moment where the audio thread has just iterated it and has just started processing. The
2454caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which
2455includes the cost of recursively processing its inputs. This is the biggest compromise made with
2456the approach taken by miniaudio for its lock-free processing system. The cost of detaching nodes
2457earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching
2458higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass
2459detachments, detach starting from the lowest level nodes and work your way towards the final
2460endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not
2461running, detachment will be fast and detachment in any order will be the same. The reason nodes
2462need to wait for their input attachments to complete is due to the potential for desyncs between
2463data sources. If the node was to terminate processing mid way through processing its inputs,
2464there's a chance that some of the underlying data sources will have been read, but then others not.
2465That will then result in a potential desynchronization when detaching and reattaching higher-level
2466nodes. A possible solution to this is to have an option when detaching to terminate processing
2467before processing all input attachments which should be fairly simple.
2468
2469Another compromise, albeit less significant, is locking when attaching and detaching nodes. This
2470locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present
2471for each input bus and output bus. When an output bus is connected to an input bus, both the output
2472bus and input bus is locked. This locking is specifically for attaching and detaching across
2473different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and
2474unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when
2475considering that iterating over attachments must not break as a result of attaching or detaching a
2476node while iteration is occurring.
2477
2478Attaching and detaching are both quite simple. When an output bus of a node is attached to an input
2479bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where
2480each item in the list is and output bus. We have some intentional (and convenient) restrictions on
2481what can done with the linked list in order to simplify the implementation. First of all, whenever
2482something needs to iterate over the list, it must do so in a forward direction. Backwards iteration
2483is not supported. Also, items can only be added to the start of the list.
2484
2485The linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer
2486to the next item in the list, and another to the previous item. A pointer to the previous item is
2487only required for fast detachment of the node - it is never used in iteration. This is an
2488important property because it means from the perspective of iteration, attaching and detaching of
2489an item can be done with a single atomic assignment. This is exploited by both the attachment and
2490detachment process. When attaching the node, the first thing that is done is the setting of the
2491local "next" and "previous" pointers of the node. After that, the item is "attached" to the list
2492by simply performing an atomic exchange with the head pointer. After that, the node is "attached"
2493to the list from the perspective of iteration. Even though the "previous" pointer of the next item
2494hasn't yet been set, from the perspective of iteration it's been attached because iteration will
2495only be happening in a forward direction which means the "previous" pointer won't actually ever get
2496used. The same general process applies to detachment. See `ma_node_attach_output_bus()` and
2497`ma_node_detach_output_bus()` for the implementation of this mechanism.
2498
2499
2500
25018. Decoding
2502===========
2503The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from
2504devices and can be used independently. Built-in support is included for the following formats:
2505
2506 +---------+
2507 | Format |
2508 +---------+
2509 | WAV |
2510 | MP3 |
2511 | FLAC |
2512 +---------+
2513
2514You can disable the built-in decoders by specifying one or more of the following options before the
2515miniaudio implementation:
2516
2517 ```c
2518 #define MA_NO_WAV
2519 #define MA_NO_MP3
2520 #define MA_NO_FLAC
2521 ```
2522
2523miniaudio supports the ability to plug in custom decoders. See the section below for details on how
2524to use custom decoders.
2525
2526A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with
2527`ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is
2528an example for loading a decoder from a file:
2529
2530 ```c
2531 ma_decoder decoder;
2532 ma_result result = ma_decoder_init_file("MySong.mp3", NULL, &decoder);
2533 if (result != MA_SUCCESS) {
2534 return false; // An error occurred.
2535 }
2536
2537 ...
2538
2539 ma_decoder_uninit(&decoder);
2540 ```
2541
2542When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object
2543(the `NULL` argument in the example above) which allows you to configure the output format, channel
2544count, sample rate and channel map:
2545
2546 ```c
2547 ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000);
2548 ```
2549
2550When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the
2551same as that defined by the decoding backend.
2552
2553Data is read from the decoder as PCM frames. This will output the number of PCM frames actually
2554read. If this is less than the requested number of PCM frames it means you've reached the end. The
2555return value will be `MA_AT_END` if no samples have been read and the end has been reached.
2556
2557 ```c
2558 ma_result result = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead, &framesRead);
2559 if (framesRead < framesToRead) {
2560 // Reached the end.
2561 }
2562 ```
2563
2564You can also seek to a specific frame like so:
2565
2566 ```c
2567 ma_result result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame);
2568 if (result != MA_SUCCESS) {
2569 return false; // An error occurred.
2570 }
2571 ```
2572
2573If you want to loop back to the start, you can simply seek back to the first PCM frame:
2574
2575 ```c
2576 ma_decoder_seek_to_pcm_frame(pDecoder, 0);
2577 ```
2578
2579When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding
2580backend. This can be unnecessarily inefficient if the type is already known. In this case you can
2581use `encodingFormat` variable in the device config to specify a specific encoding format you want
2582to decode:
2583
2584 ```c
2585 decoderConfig.encodingFormat = ma_encoding_format_wav;
2586 ```
2587
2588See the `ma_encoding_format` enum for possible encoding formats.
2589
2590The `ma_decoder_init_file()` API will try using the file extension to determine which decoding
2591backend to prefer.
2592
2593
25948.1. Custom Decoders
2595--------------------
2596It's possible to implement a custom decoder and plug it into miniaudio. This is extremely useful
2597when you want to use the `ma_decoder` API, but need to support an encoding format that's not one of
2598the stock formats supported by miniaudio. This can be put to particularly good use when using the
2599`ma_engine` and/or `ma_resource_manager` APIs because they use `ma_decoder` internally. If, for
2600example, you wanted to support Opus, you can do so with a custom decoder (there if a reference
2601Opus decoder in the "extras" folder of the miniaudio repository which uses libopus + libopusfile).
2602
2603A custom decoder must implement a data source. A vtable called `ma_decoding_backend_vtable` needs
2604to be implemented which is then passed into the decoder config:
2605
2606 ```c
2607 ma_decoding_backend_vtable* pCustomBackendVTables[] =
2608 {
2609 &g_ma_decoding_backend_vtable_libvorbis,
2610 &g_ma_decoding_backend_vtable_libopus
2611 };
2612
2613 ...
2614
2615 decoderConfig = ma_decoder_config_init_default();
2616 decoderConfig.pCustomBackendUserData = NULL;
2617 decoderConfig.ppCustomBackendVTables = pCustomBackendVTables;
2618 decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);
2619 ```
2620
2621The `ma_decoding_backend_vtable` vtable has the following functions:
2622
2623 ```
2624 onInit
2625 onInitFile
2626 onInitFileW
2627 onInitMemory
2628 onUninit
2629 ```
2630
2631There are only two functions that must be implemented - `onInit` and `onUninit`. The other
2632functions can be implemented for a small optimization for loading from a file path or memory. If
2633these are not specified, miniaudio will deal with it for you via a generic implementation.
2634
2635When you initialize a custom data source (by implementing the `onInit` function in the vtable) you
2636will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the
2637section about data sources for details on how to implement this. Alternatively, see the
2638"custom_decoders" example in the miniaudio repository.
2639
2640The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data
2641from some arbitrary source. You'll use these functions to read from the raw data and perform the
2642decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant
2643parameter.
2644
2645The `pConfig` parameter in `onInit` can be used to configure the backend if appropriate. It's only
2646used as a hint and can be ignored. However, if any of the properties are relevant to your decoder,
2647an optimal implementation will handle the relevant properties appropriately.
2648
2649If memory allocation is required, it should be done so via the specified allocation callbacks if
2650possible (the `pAllocationCallbacks` parameter).
2651
2652If an error occurs when initializing the decoder, you should leave `ppBackend` unset, or set to
2653NULL, and make sure everything is cleaned up appropriately and an appropriate result code returned.
2654When multiple custom backends are specified, miniaudio will cycle through the vtables in the order
2655they're listed in the array that's passed into the decoder config so it's important that your
2656initialization routine is clean.
2657
2658When a decoder is uninitialized, the `onUninit` callback will be fired which will give you an
2659opportunity to clean up and internal data.
2660
2661
2662
26639. Encoding
2664===========
2665The `ma_encoding` API is used for writing audio files. The only supported output format is WAV.
2666This can be disabled by specifying the following option before the implementation of miniaudio:
2667
2668 ```c
2669 #define MA_NO_WAV
2670 ```
2671
2672An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data
2673delivered via callbacks with `ma_encoder_init()`. Below is an example for initializing an encoder
2674to output to a file.
2675
2676 ```c
2677 ma_encoder_config config = ma_encoder_config_init(ma_encoding_format_wav, FORMAT, CHANNELS, SAMPLE_RATE);
2678 ma_encoder encoder;
2679 ma_result result = ma_encoder_init_file("my_file.wav", &config, &encoder);
2680 if (result != MA_SUCCESS) {
2681 // Error
2682 }
2683
2684 ...
2685
2686 ma_encoder_uninit(&encoder);
2687 ```
2688
2689When initializing an encoder you must specify a config which is initialized with
2690`ma_encoder_config_init()`. Here you must specify the file type, the output sample format, output
2691channel count and output sample rate. The following file types are supported:
2692
2693 +------------------------+-------------+
2694 | Enum | Description |
2695 +------------------------+-------------+
2696 | ma_encoding_format_wav | WAV |
2697 +------------------------+-------------+
2698
2699If the format, channel count or sample rate is not supported by the output file type an error will
2700be returned. The encoder will not perform data conversion so you will need to convert it before
2701outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the
2702example below:
2703
2704 ```c
2705 ma_uint64 framesWritten;
2706 result = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite, &framesWritten);
2707 if (result != MA_SUCCESS) {
2708 ... handle error ...
2709 }
2710 ```
2711
2712The `framesWritten` variable will contain the number of PCM frames that were actually written. This
2713is optionally and you can pass in `NULL` if you need this.
2714
2715Encoders must be uninitialized with `ma_encoder_uninit()`.
2716
2717
2718
271910. Data Conversion
2720===================
2721A data conversion API is included with miniaudio which supports the majority of data conversion
2722requirements. This supports conversion between sample formats, channel counts (with channel
2723mapping) and sample rates.
2724
2725
272610.1. Sample Format Conversion
2727------------------------------
2728Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and
2729`ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()` to convert between two specific
2730formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use
2731`ma_convert_pcm_frames_format()` to convert PCM frames where you want to specify the frame count
2732and channel count as a variable instead of the total sample count.
2733
2734
273510.1.1. Dithering
2736-----------------
2737Dithering can be set using the ditherMode parameter.
2738
2739The different dithering modes include the following, in order of efficiency:
2740
2741 +-----------+--------------------------+
2742 | Type | Enum Token |
2743 +-----------+--------------------------+
2744 | None | ma_dither_mode_none |
2745 | Rectangle | ma_dither_mode_rectangle |
2746 | Triangle | ma_dither_mode_triangle |
2747 +-----------+--------------------------+
2748
2749Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be
2750ignored for conversions where dithering is not needed. Dithering is available for the following
2751conversions:
2752
2753 ```
2754 s16 -> u8
2755 s24 -> u8
2756 s32 -> u8
2757 f32 -> u8
2758 s24 -> s16
2759 s32 -> s16
2760 f32 -> s16
2761 ```
2762
2763Note that it is not an error to pass something other than ma_dither_mode_none for conversions where
2764dither is not used. It will just be ignored.
2765
2766
2767
276810.2. Channel Conversion
2769------------------------
2770Channel conversion is used for channel rearrangement and conversion from one channel count to
2771another. The `ma_channel_converter` API is used for channel conversion. Below is an example of
2772initializing a simple channel converter which converts from mono to stereo.
2773
2774 ```c
2775 ma_channel_converter_config config = ma_channel_converter_config_init(
2776 ma_format, // Sample format
2777 1, // Input channels
2778 NULL, // Input channel map
2779 2, // Output channels
2780 NULL, // Output channel map
2781 ma_channel_mix_mode_default); // The mixing algorithm to use when combining channels.
2782
2783 result = ma_channel_converter_init(&config, NULL, &converter);
2784 if (result != MA_SUCCESS) {
2785 // Error.
2786 }
2787 ```
2788
2789To perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so:
2790
2791 ```c
2792 ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount);
2793 if (result != MA_SUCCESS) {
2794 // Error.
2795 }
2796 ```
2797
2798It is up to the caller to ensure the output buffer is large enough to accommodate the new PCM
2799frames.
2800
2801Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported.
2802
2803
280410.2.1. Channel Mapping
2805-----------------------
2806In addition to converting from one channel count to another, like the example above, the channel
2807converter can also be used to rearrange channels. When initializing the channel converter, you can
2808optionally pass in channel maps for both the input and output frames. If the channel counts are the
2809same, and each channel map contains the same channel positions with the exception that they're in
2810a different order, a simple shuffling of the channels will be performed. If, however, there is not
2811a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed
2812based on a mixing mode which is specified when initializing the `ma_channel_converter_config`
2813object.
2814
2815When converting from mono to multi-channel, the mono channel is simply copied to each output
2816channel. When going the other way around, the audio of each output channel is simply averaged and
2817copied to the mono channel.
2818
2819In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess
2820channels and silence extra channels. For example, converting from 4 to 2 channels, the 3rd and 4th
2821channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and
28224th channels.
2823
2824The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a
2825simple distribution between input and output. Imagine sitting in the middle of a room, with
2826speakers on the walls representing channel positions. The `MA_CHANNEL_FRONT_LEFT` position can be
2827thought of as being in the corner of the front and left walls.
2828
2829Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined
2830weights. Custom weights can be passed in as the last parameter of
2831`ma_channel_converter_config_init()`.
2832
2833Predefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a
2834`ma_standard_channel_map` enum as its first parameter, which can be one of the following:
2835
2836 +-----------------------------------+-----------------------------------------------------------+
2837 | Name | Description |
2838 +-----------------------------------+-----------------------------------------------------------+
2839 | ma_standard_channel_map_default | Default channel map used by miniaudio. See below. |
2840 | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps. |
2841 | ma_standard_channel_map_alsa | Default ALSA channel map. |
2842 | ma_standard_channel_map_rfc3551 | RFC 3551. Based on AIFF. |
2843 | ma_standard_channel_map_flac | FLAC channel map. |
2844 | ma_standard_channel_map_vorbis | Vorbis channel map. |
2845 | ma_standard_channel_map_sound4 | FreeBSD's sound(4). |
2846 | ma_standard_channel_map_sndio | sndio channel map. http://www.sndio.org/tips.html. |
2847 | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering |
2848 +-----------------------------------+-----------------------------------------------------------+
2849
2850Below are the channel maps used by default in miniaudio (`ma_standard_channel_map_default`):
2851
2852 +---------------+---------------------------------+
2853 | Channel Count | Mapping |
2854 +---------------+---------------------------------+
2855 | 1 (Mono) | 0: MA_CHANNEL_MONO |
2856 +---------------+---------------------------------+
2857 | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT <br> |
2858 | | 1: MA_CHANNEL_FRONT_RIGHT |
2859 +---------------+---------------------------------+
2860 | 3 | 0: MA_CHANNEL_FRONT_LEFT <br> |
2861 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2862 | | 2: MA_CHANNEL_FRONT_CENTER |
2863 +---------------+---------------------------------+
2864 | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT <br> |
2865 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2866 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
2867 | | 3: MA_CHANNEL_BACK_CENTER |
2868 +---------------+---------------------------------+
2869 | 5 | 0: MA_CHANNEL_FRONT_LEFT <br> |
2870 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2871 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
2872 | | 3: MA_CHANNEL_BACK_LEFT <br> |
2873 | | 4: MA_CHANNEL_BACK_RIGHT |
2874 +---------------+---------------------------------+
2875 | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT <br> |
2876 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2877 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
2878 | | 3: MA_CHANNEL_LFE <br> |
2879 | | 4: MA_CHANNEL_SIDE_LEFT <br> |
2880 | | 5: MA_CHANNEL_SIDE_RIGHT |
2881 +---------------+---------------------------------+
2882 | 7 | 0: MA_CHANNEL_FRONT_LEFT <br> |
2883 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2884 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
2885 | | 3: MA_CHANNEL_LFE <br> |
2886 | | 4: MA_CHANNEL_BACK_CENTER <br> |
2887 | | 4: MA_CHANNEL_SIDE_LEFT <br> |
2888 | | 5: MA_CHANNEL_SIDE_RIGHT |
2889 +---------------+---------------------------------+
2890 | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT <br> |
2891 | | 1: MA_CHANNEL_FRONT_RIGHT <br> |
2892 | | 2: MA_CHANNEL_FRONT_CENTER <br> |
2893 | | 3: MA_CHANNEL_LFE <br> |
2894 | | 4: MA_CHANNEL_BACK_LEFT <br> |
2895 | | 5: MA_CHANNEL_BACK_RIGHT <br> |
2896 | | 6: MA_CHANNEL_SIDE_LEFT <br> |
2897 | | 7: MA_CHANNEL_SIDE_RIGHT |
2898 +---------------+---------------------------------+
2899 | Other | All channels set to 0. This |
2900 | | is equivalent to the same |
2901 | | mapping as the device. |
2902 +---------------+---------------------------------+
2903
2904
2905
290610.3. Resampling
2907----------------
2908Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something
2909like the following:
2910
2911 ```c
2912 ma_resampler_config config = ma_resampler_config_init(
2913 ma_format_s16,
2914 channels,
2915 sampleRateIn,
2916 sampleRateOut,
2917 ma_resample_algorithm_linear);
2918
2919 ma_resampler resampler;
2920 ma_result result = ma_resampler_init(&config, NULL, &resampler);
2921 if (result != MA_SUCCESS) {
2922 // An error occurred...
2923 }
2924 ```
2925
2926Do the following to uninitialize the resampler:
2927
2928 ```c
2929 ma_resampler_uninit(&resampler);
2930 ```
2931
2932The following example shows how data can be processed
2933
2934 ```c
2935 ma_uint64 frameCountIn = 1000;
2936 ma_uint64 frameCountOut = 2000;
2937 ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
2938 if (result != MA_SUCCESS) {
2939 // An error occurred...
2940 }
2941
2942 // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the
2943 // number of output frames written.
2944 ```
2945
2946To initialize the resampler you first need to set up a config (`ma_resampler_config`) with
2947`ma_resampler_config_init()`. You need to specify the sample format you want to use, the number of
2948channels, the input and output sample rate, and the algorithm.
2949
2950The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format
2951you will need to perform pre- and post-conversions yourself where necessary. Note that the format
2952is the same for both input and output. The format cannot be changed after initialization.
2953
2954The resampler supports multiple channels and is always interleaved (both input and output). The
2955channel count cannot be changed after initialization.
2956
2957The sample rates can be anything other than zero, and are always specified in hertz. They should be
2958set to something like 44100, etc. The sample rate is the only configuration property that can be
2959changed after initialization.
2960
2961The miniaudio resampler has built-in support for the following algorithms:
2962
2963 +-----------+------------------------------+
2964 | Algorithm | Enum Token |
2965 +-----------+------------------------------+
2966 | Linear | ma_resample_algorithm_linear |
2967 | Custom | ma_resample_algorithm_custom |
2968 +-----------+------------------------------+
2969
2970The algorithm cannot be changed after initialization.
2971
2972Processing always happens on a per PCM frame basis and always assumes interleaved input and output.
2973De-interleaved processing is not supported. To process frames, use
2974`ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you
2975can fit in the output buffer and the number of input frames contained in the input buffer. On
2976output these variables contain the number of output frames that were written to the output buffer
2977and the number of input frames that were consumed in the process. You can pass in NULL for the
2978input buffer in which case it will be treated as an infinitely large buffer of zeros. The output
2979buffer can also be NULL, in which case the processing will be treated as seek.
2980
2981The sample rate can be changed dynamically on the fly. You can change this with explicit sample
2982rates with `ma_resampler_set_rate()` and also with a decimal ratio with
2983`ma_resampler_set_rate_ratio()`. The ratio is in/out.
2984
2985Sometimes it's useful to know exactly how many input frames will be required to output a specific
2986number of frames. You can calculate this with `ma_resampler_get_required_input_frame_count()`.
2987Likewise, it's sometimes useful to know exactly how many frames would be output given a certain
2988number of input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`.
2989
2990Due to the nature of how resampling works, the resampler introduces some latency. This can be
2991retrieved in terms of both the input rate and the output rate with
2992`ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`.
2993
2994
299510.3.1. Resampling Algorithms
2996-----------------------------
2997The choice of resampling algorithm depends on your situation and requirements.
2998
2999
300010.3.1.1. Linear Resampling
3001---------------------------
3002The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however,
3003some control over the quality of the linear resampler which may make it a suitable option depending
3004on your requirements.
3005
3006The linear resampler performs low-pass filtering before or after downsampling or upsampling,
3007depending on the sample rates you're converting between. When decreasing the sample rate, the
3008low-pass filter will be applied before downsampling. When increasing the rate it will be performed
3009after upsampling. By default a fourth order low-pass filter will be applied. This can be configured
3010via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering.
3011
3012The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of
3013the input and output sample rates (Nyquist Frequency).
3014
3015The API for the linear resampler is the same as the main resampler API, only it's called
3016`ma_linear_resampler`.
3017
3018
301910.3.2. Custom Resamplers
3020-------------------------
3021You can implement a custom resampler by using the `ma_resample_algorithm_custom` resampling
3022algorithm and setting a vtable in the resampler config:
3023
3024 ```c
3025 ma_resampler_config config = ma_resampler_config_init(..., ma_resample_algorithm_custom);
3026 config.pBackendVTable = &g_customResamplerVTable;
3027 ```
3028
3029Custom resamplers are useful if the stock algorithms are not appropriate for your use case. You
3030need to implement the required functions in `ma_resampling_backend_vtable`. Note that not all
3031functions in the vtable need to be implemented, but if it's possible to implement, they should be.
3032
3033You can use the `ma_linear_resampler` object for an example on how to implement the vtable. The
3034`onGetHeapSize` callback is used to calculate the size of any internal heap allocation the custom
3035resampler will need to make given the supplied config. When you initialize the resampler via the
3036`onInit` callback, you'll be given a pointer to a heap allocation which is where you should store
3037the heap allocated data. You should not free this data in `onUninit` because miniaudio will manage
3038it for you.
3039
3040The `onProcess` callback is where the actual resampling takes place. On input, `pFrameCountIn`
3041points to a variable containing the number of frames in the `pFramesIn` buffer and
3042`pFrameCountOut` points to a variable containing the capacity in frames of the `pFramesOut` buffer.
3043On output, `pFrameCountIn` should be set to the number of input frames that were fully consumed,
3044whereas `pFrameCountOut` should be set to the number of frames that were written to `pFramesOut`.
3045
3046The `onSetRate` callback is optional and is used for dynamically changing the sample rate. If
3047dynamic rate changes are not supported, you can set this callback to NULL.
3048
3049The `onGetInputLatency` and `onGetOutputLatency` functions are used for retrieving the latency in
3050input and output rates respectively. These can be NULL in which case latency calculations will be
3051assumed to be NULL.
3052
3053The `onGetRequiredInputFrameCount` callback is used to give miniaudio a hint as to how many input
3054frames are required to be available to produce the given number of output frames. Likewise, the
3055`onGetExpectedOutputFrameCount` callback is used to determine how many output frames will be
3056produced given the specified number of input frames. miniaudio will use these as a hint, but they
3057are optional and can be set to NULL if you're unable to implement them.
3058
3059
3060
306110.4. General Data Conversion
3062-----------------------------
3063The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and
3064resampling into one operation. This is what miniaudio uses internally to convert between the format
3065requested when the device was initialized and the format of the backend's native device. The API
3066for general data conversion is very similar to the resampling API. Create a `ma_data_converter`
3067object like this:
3068
3069 ```c
3070 ma_data_converter_config config = ma_data_converter_config_init(
3071 inputFormat,
3072 outputFormat,
3073 inputChannels,
3074 outputChannels,
3075 inputSampleRate,
3076 outputSampleRate
3077 );
3078
3079 ma_data_converter converter;
3080 ma_result result = ma_data_converter_init(&config, NULL, &converter);
3081 if (result != MA_SUCCESS) {
3082 // An error occurred...
3083 }
3084 ```
3085
3086In the example above we use `ma_data_converter_config_init()` to initialize the config, however
3087there's many more properties that can be configured, such as channel maps and resampling quality.
3088Something like the following may be more suitable depending on your requirements:
3089
3090 ```c
3091 ma_data_converter_config config = ma_data_converter_config_init_default();
3092 config.formatIn = inputFormat;
3093 config.formatOut = outputFormat;
3094 config.channelsIn = inputChannels;
3095 config.channelsOut = outputChannels;
3096 config.sampleRateIn = inputSampleRate;
3097 config.sampleRateOut = outputSampleRate;
3098 ma_channel_map_init_standard(ma_standard_channel_map_flac, config.channelMapIn, sizeof(config.channelMapIn)/sizeof(config.channelMapIn[0]), config.channelCountIn);
3099 config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER;
3100 ```
3101
3102Do the following to uninitialize the data converter:
3103
3104 ```c
3105 ma_data_converter_uninit(&converter, NULL);
3106 ```
3107
3108The following example shows how data can be processed
3109
3110 ```c
3111 ma_uint64 frameCountIn = 1000;
3112 ma_uint64 frameCountOut = 2000;
3113 ma_result result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut);
3114 if (result != MA_SUCCESS) {
3115 // An error occurred...
3116 }
3117
3118 // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number
3119 // of output frames written.
3120 ```
3121
3122The data converter supports multiple channels and is always interleaved (both input and output).
3123The channel count cannot be changed after initialization.
3124
3125Sample rates can be anything other than zero, and are always specified in hertz. They should be set
3126to something like 44100, etc. The sample rate is the only configuration property that can be
3127changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of
3128`ma_data_converter_config` is set to `MA_TRUE`. To change the sample rate, use
3129`ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out.
3130The resampling algorithm cannot be changed after initialization.
3131
3132Processing always happens on a per PCM frame basis and always assumes interleaved input and output.
3133De-interleaved processing is not supported. To process frames, use
3134`ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames
3135you can fit in the output buffer and the number of input frames contained in the input buffer. On
3136output these variables contain the number of output frames that were written to the output buffer
3137and the number of input frames that were consumed in the process. You can pass in NULL for the
3138input buffer in which case it will be treated as an infinitely large
3139buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated
3140as seek.
3141
3142Sometimes it's useful to know exactly how many input frames will be required to output a specific
3143number of frames. You can calculate this with `ma_data_converter_get_required_input_frame_count()`.
3144Likewise, it's sometimes useful to know exactly how many frames would be output given a certain
3145number of input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`.
3146
3147Due to the nature of how resampling works, the data converter introduces some latency if resampling
3148is required. This can be retrieved in terms of both the input rate and the output rate with
3149`ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`.
3150
3151
3152
315311. Filtering
3154=============
3155
315611.1. Biquad Filtering
3157----------------------
3158Biquad filtering is achieved with the `ma_biquad` API. Example:
3159
3160 ```c
3161 ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);
3162 ma_result result = ma_biquad_init(&config, NULL, &biquad);
3163 if (result != MA_SUCCESS) {
3164 // Error.
3165 }
3166
3167 ...
3168
3169 ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount);
3170 ```
3171
3172Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0,
3173b1 and b2, and the denominator coefficients are a0, a1 and a2. The a0 coefficient is required and
3174coefficients must not be pre-normalized.
3175
3176Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format
3177you need to convert it yourself beforehand. When using `ma_format_s16` the biquad filter will use
3178fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used.
3179
3180Input and output frames are always interleaved.
3181
3182Filtering can be applied in-place by passing in the same pointer for both the input and output
3183buffers, like so:
3184
3185 ```c
3186 ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount);
3187 ```
3188
3189If you need to change the values of the coefficients, but maintain the values in the registers you
3190can do so with `ma_biquad_reinit()`. This is useful if you need to change the properties of the
3191filter while keeping the values of registers valid to avoid glitching. Do not use
3192`ma_biquad_init()` for this as it will do a full initialization which involves clearing the
3193registers to 0. Note that changing the format or channel count after initialization is invalid and
3194will result in an error.
3195
3196
319711.2. Low-Pass Filtering
3198------------------------
3199Low-pass filtering is achieved with the following APIs:
3200
3201 +---------+------------------------------------------+
3202 | API | Description |
3203 +---------+------------------------------------------+
3204 | ma_lpf1 | First order low-pass filter |
3205 | ma_lpf2 | Second order low-pass filter |
3206 | ma_lpf | High order low-pass filter (Butterworth) |
3207 +---------+------------------------------------------+
3208
3209Low-pass filter example:
3210
3211 ```c
3212 ma_lpf_config config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
3213 ma_result result = ma_lpf_init(&config, &lpf);
3214 if (result != MA_SUCCESS) {
3215 // Error.
3216 }
3217
3218 ...
3219
3220 ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount);
3221 ```
3222
3223Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format
3224you need to convert it yourself beforehand. Input and output frames are always interleaved.
3225
3226Filtering can be applied in-place by passing in the same pointer for both the input and output
3227buffers, like so:
3228
3229 ```c
3230 ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount);
3231 ```
3232
3233The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more,
3234you can chain first and second order filters together.
3235
3236 ```c
3237 for (iFilter = 0; iFilter < filterCount; iFilter += 1) {
3238 ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount);
3239 }
3240 ```
3241
3242If you need to change the configuration of the filter, but need to maintain the state of internal
3243registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample
3244rate and/or cutoff frequency dynamically while maintaining smooth transitions. Note that changing the
3245format or channel count after initialization is invalid and will result in an error.
3246
3247The `ma_lpf` object supports a configurable order, but if you only need a first order filter you
3248may want to consider using `ma_lpf1`. Likewise, if you only need a second order filter you can use
3249`ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient.
3250
3251If an even filter order is specified, a series of second order filters will be processed in a
3252chain. If an odd filter order is specified, a first order filter will be applied, followed by a
3253series of second order filters in a chain.
3254
3255
325611.3. High-Pass Filtering
3257-------------------------
3258High-pass filtering is achieved with the following APIs:
3259
3260 +---------+-------------------------------------------+
3261 | API | Description |
3262 +---------+-------------------------------------------+
3263 | ma_hpf1 | First order high-pass filter |
3264 | ma_hpf2 | Second order high-pass filter |
3265 | ma_hpf | High order high-pass filter (Butterworth) |
3266 +---------+-------------------------------------------+
3267
3268High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`,
3269`ma_hpf2` and `ma_hpf`. See example code for low-pass filters for example usage.
3270
3271
327211.4. Band-Pass Filtering
3273-------------------------
3274Band-pass filtering is achieved with the following APIs:
3275
3276 +---------+-------------------------------+
3277 | API | Description |
3278 +---------+-------------------------------+
3279 | ma_bpf2 | Second order band-pass filter |
3280 | ma_bpf | High order band-pass filter |
3281 +---------+-------------------------------+
3282
3283Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and
3284`ma_hpf`. See example code for low-pass filters for example usage. Note that the order for
3285band-pass filters must be an even number which means there is no first order band-pass filter,
3286unlike low-pass and high-pass filters.
3287
3288
328911.5. Notch Filtering
3290---------------------
3291Notch filtering is achieved with the following APIs:
3292
3293 +-----------+------------------------------------------+
3294 | API | Description |
3295 +-----------+------------------------------------------+
3296 | ma_notch2 | Second order notching filter |
3297 +-----------+------------------------------------------+
3298
3299
330011.6. Peaking EQ Filtering
3301-------------------------
3302Peaking filtering is achieved with the following APIs:
3303
3304 +----------+------------------------------------------+
3305 | API | Description |
3306 +----------+------------------------------------------+
3307 | ma_peak2 | Second order peaking filter |
3308 +----------+------------------------------------------+
3309
3310
331111.7. Low Shelf Filtering
3312-------------------------
3313Low shelf filtering is achieved with the following APIs:
3314
3315 +-------------+------------------------------------------+
3316 | API | Description |
3317 +-------------+------------------------------------------+
3318 | ma_loshelf2 | Second order low shelf filter |
3319 +-------------+------------------------------------------+
3320
3321Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to
3322just turn them down rather than eliminate them entirely.
3323
3324
332511.8. High Shelf Filtering
3326--------------------------
3327High shelf filtering is achieved with the following APIs:
3328
3329 +-------------+------------------------------------------+
3330 | API | Description |
3331 +-------------+------------------------------------------+
3332 | ma_hishelf2 | Second order high shelf filter |
3333 +-------------+------------------------------------------+
3334
3335The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf`
3336instead of `ma_loshelf`. Where a low shelf filter is used to adjust the volume of low frequencies,
3337the high shelf filter does the same thing for high frequencies.
3338
3339
3340
3341
334212. Waveform and Noise Generation
3343=================================
3344
334512.1. Waveforms
3346---------------
3347miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved
3348with the `ma_waveform` API. Example:
3349
3350 ```c
3351 ma_waveform_config config = ma_waveform_config_init(
3352 FORMAT,
3353 CHANNELS,
3354 SAMPLE_RATE,
3355 ma_waveform_type_sine,
3356 amplitude,
3357 frequency);
3358
3359 ma_waveform waveform;
3360 ma_result result = ma_waveform_init(&config, &waveform);
3361 if (result != MA_SUCCESS) {
3362 // Error.
3363 }
3364
3365 ...
3366
3367 ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount);
3368 ```
3369
3370The amplitude, frequency, type, and sample rate can be changed dynamically with
3371`ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, `ma_waveform_set_type()`, and
3372`ma_waveform_set_sample_rate()` respectively.
3373
3374You can invert the waveform by setting the amplitude to a negative value. You can use this to
3375control whether or not a sawtooth has a positive or negative ramp, for example.
3376
3377Below are the supported waveform types:
3378
3379 +---------------------------+
3380 | Enum Name |
3381 +---------------------------+
3382 | ma_waveform_type_sine |
3383 | ma_waveform_type_square |
3384 | ma_waveform_type_triangle |
3385 | ma_waveform_type_sawtooth |
3386 +---------------------------+
3387
3388
3389
339012.2. Noise
3391-----------
3392miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example:
3393
3394 ```c
3395 ma_noise_config config = ma_noise_config_init(
3396 FORMAT,
3397 CHANNELS,
3398 ma_noise_type_white,
3399 SEED,
3400 amplitude);
3401
3402 ma_noise noise;
3403 ma_result result = ma_noise_init(&config, &noise);
3404 if (result != MA_SUCCESS) {
3405 // Error.
3406 }
3407
3408 ...
3409
3410 ma_noise_read_pcm_frames(&noise, pOutput, frameCount);
3411 ```
3412
3413The noise API uses simple LCG random number generation. It supports a custom seed which is useful
3414for things like automated testing requiring reproducibility. Setting the seed to zero will default
3415to `MA_DEFAULT_LCG_SEED`.
3416
3417The amplitude and seed can be changed dynamically with `ma_noise_set_amplitude()` and
3418`ma_noise_set_seed()` respectively.
3419
3420By default, the noise API will use different values for different channels. So, for example, the
3421left side in a stereo stream will be different to the right side. To instead have each channel use
3422the same random value, set the `duplicateChannels` member of the noise config to true, like so:
3423
3424 ```c
3425 config.duplicateChannels = MA_TRUE;
3426 ```
3427
3428Below are the supported noise types.
3429
3430 +------------------------+
3431 | Enum Name |
3432 +------------------------+
3433 | ma_noise_type_white |
3434 | ma_noise_type_pink |
3435 | ma_noise_type_brownian |
3436 +------------------------+
3437
3438
3439
344013. Audio Buffers
3441=================
3442miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can
3443read from memory that's managed by the application, but can also handle the memory management for
3444you internally. Memory management is flexible and should support most use cases.
3445
3446Audio buffers are initialized using the standard configuration system used everywhere in miniaudio:
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 buffer;
3457 result = ma_audio_buffer_init(&config, &buffer);
3458 if (result != MA_SUCCESS) {
3459 // Error.
3460 }
3461
3462 ...
3463
3464 ma_audio_buffer_uninit(&buffer);
3465 ```
3466
3467In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an
3468application can do self-managed memory allocation. If you would rather make a copy of the data, use
3469`ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`.
3470
3471Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the
3472raw audio data in a contiguous block of memory. That is, the raw audio data will be located
3473immediately after the `ma_audio_buffer` structure. To do this, use
3474`ma_audio_buffer_alloc_and_init()`:
3475
3476 ```c
3477 ma_audio_buffer_config config = ma_audio_buffer_config_init(
3478 format,
3479 channels,
3480 sizeInFrames,
3481 pExistingData,
3482 &allocationCallbacks);
3483
3484 ma_audio_buffer* pBuffer
3485 result = ma_audio_buffer_alloc_and_init(&config, &pBuffer);
3486 if (result != MA_SUCCESS) {
3487 // Error
3488 }
3489
3490 ...
3491
3492 ma_audio_buffer_uninit_and_free(&buffer);
3493 ```
3494
3495If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it
3496with `ma_audio_buffer_uninit_and_free()`. In the example above, the memory pointed to by
3497`pExistingData` will be copied into the buffer, which is contrary to the behavior of
3498`ma_audio_buffer_init()`.
3499
3500An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the
3501cursor moves forward. The last parameter (`loop`) can be used to determine if the buffer should
3502loop. The return value is the number of frames actually read. If this is less than the number of
3503frames requested it means the end has been reached. This should never happen if the `loop`
3504parameter is set to true. If you want to manually loop back to the start, you can do so with with
3505`ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an
3506audio buffer.
3507
3508 ```c
3509 ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping);
3510 if (framesRead < desiredFrameCount) {
3511 // If not looping, this means the end has been reached. This should never happen in looping mode with valid input.
3512 }
3513 ```
3514
3515Sometimes you may want to avoid the cost of data movement between the internal buffer and the
3516output buffer. Instead you can use memory mapping to retrieve a pointer to a segment of data:
3517
3518 ```c
3519 void* pMappedFrames;
3520 ma_uint64 frameCount = frameCountToTryMapping;
3521 ma_result result = ma_audio_buffer_map(pAudioBuffer, &pMappedFrames, &frameCount);
3522 if (result == MA_SUCCESS) {
3523 // Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be
3524 // less due to the end of the buffer being reached.
3525 ma_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels);
3526
3527 // You must unmap the buffer.
3528 ma_audio_buffer_unmap(pAudioBuffer, frameCount);
3529 }
3530 ```
3531
3532When you use memory mapping, the read cursor is increment by the frame count passed in to
3533`ma_audio_buffer_unmap()`. If you decide not to process every frame you can pass in a value smaller
3534than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is
3535that it does not handle looping for you. You can determine if the buffer is at the end for the
3536purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of
3537`ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END`
3538as an error when returned by `ma_audio_buffer_unmap()`.
3539
3540
3541
354214. Ring Buffers
3543================
3544miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via
3545the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates on bytes, whereas the `ma_pcm_rb`
3546operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around
3547`ma_rb`.
3548
3549Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved
3550streams. The caller can also allocate their own backing memory for the ring buffer to use
3551internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for
3552you.
3553
3554The examples below use the PCM frame variant of the ring buffer since that's most likely the one
3555you will want to use. To initialize a ring buffer, do something like the following:
3556
3557 ```c
3558 ma_pcm_rb rb;
3559 ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb);
3560 if (result != MA_SUCCESS) {
3561 // Error
3562 }
3563 ```
3564
3565The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because
3566it's the PCM variant of the ring buffer API. For the regular ring buffer that operates on bytes you
3567would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes
3568instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter
3569is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines.
3570Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used.
3571
3572Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is
3573offset from each other based on the stride. To manage your sub-buffers you can use
3574`ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and
3575`ma_pcm_rb_get_subbuffer_ptr()`.
3576
3577Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section
3578of the ring buffer. You specify the number of frames you need, and on output it will set to what
3579was actually acquired. If the read or write pointer is positioned such that the number of frames
3580requested will require a loop, it will be clamped to the end of the buffer. Therefore, the number
3581of frames you're given may be less than the number you requested.
3582
3583After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the
3584buffer and then "commit" it with `ma_pcm_rb_commit_read()` or `ma_pcm_rb_commit_write()`. This is
3585where the read/write pointers are updated. When you commit you need to pass in the buffer that was
3586returned by the earlier call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is
3587only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and
3588`ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was
3589originally requested.
3590
3591If you want to correct for drift between the write pointer and the read pointer you can use a
3592combination of `ma_pcm_rb_pointer_distance()`, `ma_pcm_rb_seek_read()` and
3593`ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only
3594move the read pointer forward via the consumer thread, and the write pointer forward by the
3595producer thread. If there is too much space between the pointers, move the read pointer forward. If
3596there is too little space between the pointers, move the write pointer forward.
3597
3598You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb`
3599API. This is exactly the same, only you will use the `ma_rb` functions instead of `ma_pcm_rb` and
3600instead of frame counts you will pass around byte counts.
3601
3602The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most
3603significant bit being used to encode a loop flag and the internally managed buffers always being
3604aligned to `MA_SIMD_ALIGNMENT`.
3605
3606Note that the ring buffer is only thread safe when used by a single consumer thread and single
3607producer thread.
3608
3609
3610
361115. Backends
3612============
3613The following backends are supported by miniaudio. These are listed in order of default priority.
3614When no backend is specified when initializing a context or device, miniaudio will attempt to use
3615each of these backends in the order listed in the table below.
3616
3617Note that backends that are not usable by the build target will not be included in the build. For
3618example, ALSA, which is specific to Linux, will not be included in the Windows build.
3619
3620 +-------------+-----------------------+--------------------------------------------------------+
3621 | Name | Enum Name | Supported Operating Systems |
3622 +-------------+-----------------------+--------------------------------------------------------+
3623 | WASAPI | ma_backend_wasapi | Windows Vista+ |
3624 | DirectSound | ma_backend_dsound | Windows XP+ |
3625 | WinMM | ma_backend_winmm | Windows 95+ |
3626 | Core Audio | ma_backend_coreaudio | macOS, iOS |
3627 | sndio | ma_backend_sndio | OpenBSD |
3628 | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
3629 | OSS | ma_backend_oss | FreeBSD |
3630 | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
3631 | ALSA | ma_backend_alsa | Linux |
3632 | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
3633 | AAudio | ma_backend_aaudio | Android 8+ |
3634 | OpenSL ES | ma_backend_opensl | Android (API level 16+) |
3635 | Web Audio | ma_backend_webaudio | Web (via Emscripten) |
3636 | Custom | ma_backend_custom | Cross Platform |
3637 | Null | ma_backend_null | Cross Platform (not used on Web) |
3638 +-------------+-----------------------+--------------------------------------------------------+
3639
3640Some backends have some nuance details you may want to be aware of.
3641
364215.1. WASAPI
3643------------
3644- Low-latency shared mode will be disabled when using an application-defined sample rate which is
3645 different to the device's native sample rate. To work around this, set `wasapi.noAutoConvertSRC`
3646 to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing
3647 when the `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC
3648 will result in miniaudio's internal resampler being used instead which will in turn enable the
3649 use of low-latency shared mode.
3650
365115.2. PulseAudio
3652----------------
3653- If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki:
3654 https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling.
3655 Alternatively, consider using a different backend such as ALSA.
3656
365715.3. Android
3658-------------
3659- To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest:
3660 `<uses-permission android:name="android.permission.RECORD_AUDIO" />`
3661- With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a
3662 limitation with OpenSL|ES.
3663- With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration
3664 API (devices are enumerated through Java). You can however perform your own device enumeration
3665 through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it
3666 to ma_device_init().
3667- The backend API will perform resampling where possible. The reason for this as opposed to using
3668 miniaudio's built-in resampler is to take advantage of any potential device-specific
3669 optimizations the driver may implement.
3670
3671BSD
3672---
3673- The sndio backend is currently only enabled on OpenBSD builds.
3674- The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can
3675 use it.
3676
367715.4. UWP
3678---------
3679- UWP only supports default playback and capture devices.
3680- UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest):
3681
3682 ```
3683 <Package ...>
3684 ...
3685 <Capabilities>
3686 <DeviceCapability Name="microphone" />
3687 </Capabilities>
3688 </Package>
3689 ```
3690
369115.5. Web Audio / Emscripten
3692----------------------------
3693- You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build.
3694- The first time a context is initialized it will create a global object called "miniaudio" whose
3695 primary purpose is to act as a factory for device objects.
3696- Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as
3697 they've been deprecated.
3698- Google has implemented a policy in their browsers that prevent automatic media output without
3699 first receiving some kind of user input. The following web page has additional details:
3700 https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device
3701 may fail if you try to start playback without first handling some kind of user input.
3702
3703
3704
370516. Optimization Tips
3706=====================
3707See below for some tips on improving performance.
3708
370916.1. Low Level API
3710-------------------
3711- In the data callback, if your data is already clipped prior to copying it into the output buffer,
3712 set the `noClip` config option in the device config to true. This will disable miniaudio's built
3713 in clipping function.
3714- By default, miniaudio will pre-silence the data callback's output buffer. If you know that you
3715 will always write valid data to the output buffer you can disable pre-silencing by setting the
3716 `noPreSilence` config option in the device config to true.
3717
371816.2. High Level API
3719--------------------
3720- If a sound does not require doppler or pitch shifting, consider disabling pitching by
3721 initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag.
3722- If a sound does not require spatialization, disable it by initializing the sound with the
3723 `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be re-enabled again post-initialization with
3724 `ma_sound_set_spatialization_enabled()`.
3725- If you know all of your sounds will always be the same sample rate, set the engine's sample
3726 rate to match that of the sounds. Likewise, if you're using a self-managed resource manager,
3727 consider setting the decoded sample rate to match your sounds. By configuring everything to
3728 use a consistent sample rate, sample rate conversion can be avoided.
3729
3730
3731
373217. Miscellaneous Notes
3733=======================
3734- Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for
3735 WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though
3736 not all have been tested.
3737- When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This
3738 is due to 64-bit file APIs not being available.
3739*/
3740
3741#ifndef miniaudio_h
3742#define miniaudio_h
3743
3744#ifdef __cplusplus
3745extern "C" {
3746#endif
3747
3748#define MA_STRINGIFY(x) #x
3749#define MA_XSTRINGIFY(x) MA_STRINGIFY(x)
3750
3751#define MA_VERSION_MAJOR 0
3752#define MA_VERSION_MINOR 11
3753#define MA_VERSION_REVISION 22
3754#define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION)
3755
3756#if defined(_MSC_VER) && !defined(__clang__)
3757 #pragma warning(push)
3758 #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
3759 #pragma warning(disable:4214) /* nonstandard extension used: bit field types other than int */
3760 #pragma warning(disable:4324) /* structure was padded due to alignment specifier */
3761#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
3762 #pragma GCC diagnostic push
3763 #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
3764 #if defined(__clang__)
3765 #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */
3766 #endif
3767#endif
3768
3769
3770#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__) || defined(__ppc64__)
3771 #define MA_SIZEOF_PTR 8
3772#else
3773 #define MA_SIZEOF_PTR 4
3774#endif
3775
3776#include <stddef.h> /* For size_t. */
3777
3778/* Sized types. */
3779#if defined(MA_USE_STDINT)
3780 #include <stdint.h>
3781 typedef int8_t ma_int8;
3782 typedef uint8_t ma_uint8;
3783 typedef int16_t ma_int16;
3784 typedef uint16_t ma_uint16;
3785 typedef int32_t ma_int32;
3786 typedef uint32_t ma_uint32;
3787 typedef int64_t ma_int64;
3788 typedef uint64_t ma_uint64;
3789#else
3790 typedef signed char ma_int8;
3791 typedef unsigned char ma_uint8;
3792 typedef signed short ma_int16;
3793 typedef unsigned short ma_uint16;
3794 typedef signed int ma_int32;
3795 typedef unsigned int ma_uint32;
3796 #if defined(_MSC_VER) && !defined(__clang__)
3797 typedef signed __int64 ma_int64;
3798 typedef unsigned __int64 ma_uint64;
3799 #else
3800 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
3801 #pragma GCC diagnostic push
3802 #pragma GCC diagnostic ignored "-Wlong-long"
3803 #if defined(__clang__)
3804 #pragma GCC diagnostic ignored "-Wc++11-long-long"
3805 #endif
3806 #endif
3807 typedef signed long long ma_int64;
3808 typedef unsigned long long ma_uint64;
3809 #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
3810 #pragma GCC diagnostic pop
3811 #endif
3812 #endif
3813#endif /* MA_USE_STDINT */
3814
3815#if MA_SIZEOF_PTR == 8
3816 typedef ma_uint64 ma_uintptr;
3817#else
3819#endif
3820
3823#define MA_TRUE 1
3824#define MA_FALSE 0
3825
3826/* These float types are not used universally by miniaudio. It's to simplify some macro expansion for atomic types. */
3827typedef float ma_float;
3828typedef double ma_double;
3829
3830typedef void* ma_handle;
3831typedef void* ma_ptr;
3832
3833/*
3834ma_proc is annoying because when compiling with GCC we get pedantic warnings about converting
3835between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get
3836warning C4191 about "type cast between incompatible function types". To work around this I'm going
3837to use a different data type depending on the compiler.
3838*/
3839#if defined(__GNUC__)
3840typedef void (*ma_proc)(void);
3841#else
3842typedef void* ma_proc;
3843#endif
3844
3845#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED)
3846typedef ma_uint16 wchar_t;
3847#endif
3848
3849/* Define NULL for some compilers. */
3850#ifndef NULL
3851#define NULL 0
3852#endif
3853
3854#if defined(SIZE_MAX)
3855 #define MA_SIZE_MAX SIZE_MAX
3856#else
3857 #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */
3858#endif
3859
3860#define MA_UINT64_MAX (((ma_uint64)0xFFFFFFFF << 32) | (ma_uint64)0xFFFFFFFF) /* Weird shifting syntax is for VC6 compatibility. */
3861
3862
3863/* Platform/backend detection. */
3864#if defined(_WIN32) || defined(__COSMOPOLITAN__)
3865 #define MA_WIN32
3866 #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)))
3867 #define MA_WIN32_UWP
3868 #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES)
3869 #define MA_WIN32_GDK
3870 #else
3871 #define MA_WIN32_DESKTOP
3872 #endif
3873#endif
3874#if !defined(_WIN32) /* If it's not Win32, assume POSIX. */
3875 #define MA_POSIX
3876
3877 #if !defined(MA_NO_THREADING)
3878 /*
3879 Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented.
3880 You can use this to avoid including pthread.h in the header section. The downside is that it
3881 results in some fixed sized structures being declared for the various types that are used in
3882 miniaudio. The risk here is that these types might be too small for a given platform. This
3883 risk is yours to take and no support will be offered if you enable this option.
3884 */
3885 #ifndef MA_NO_PTHREAD_IN_HEADER
3886 #include <pthread.h> /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */
3887 typedef pthread_t ma_pthread_t;
3888 typedef pthread_mutex_t ma_pthread_mutex_t;
3889 typedef pthread_cond_t ma_pthread_cond_t;
3890 #else
3891 typedef ma_uintptr ma_pthread_t;
3892 typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t;
3893 typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t;
3894 #endif
3895 #endif
3896
3897 #if defined(__unix__)
3898 #define MA_UNIX
3899 #endif
3900 #if defined(__linux__)
3901 #define MA_LINUX
3902 #endif
3903 #if defined(__APPLE__)
3904 #define MA_APPLE
3905 #endif
3906 #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
3907 #define MA_BSD
3908 #endif
3909 #if defined(__ANDROID__)
3910 #define MA_ANDROID
3911 #endif
3912 #if defined(__EMSCRIPTEN__)
3913 #define MA_EMSCRIPTEN
3914 #endif
3915 #if defined(__ORBIS__)
3916 #define MA_ORBIS
3917 #endif
3918 #if defined(__PROSPERO__)
3919 #define MA_PROSPERO
3920 #endif
3921 #if defined(__NX__)
3922 #define MA_NX
3923 #endif
3924 #if defined(__3DS__)
3925 #define MA_3DS
3926 #endif
3927 #if defined(__SWITCH__)
3928 #define MA_SWITCH
3929 #endif
3930 #if defined(__BEOS__) || defined(__HAIKU__)
3931 #define MA_BEOS
3932 #endif
3933 #if defined(__HAIKU__)
3934 #define MA_HAIKU
3935 #endif
3936#endif
3937
3938#if defined(__has_c_attribute)
3939 #if __has_c_attribute(fallthrough)
3940 #define MA_FALLTHROUGH [[fallthrough]]
3941 #endif
3942#endif
3943#if !defined(MA_FALLTHROUGH) && defined(__has_attribute) && (defined(__clang__) || defined(__GNUC__))
3944 #if __has_attribute(fallthrough)
3945 #define MA_FALLTHROUGH __attribute__((fallthrough))
3946 #endif
3947#endif
3948#if !defined(MA_FALLTHROUGH)
3949 #define MA_FALLTHROUGH ((void)0)
3950#endif
3951
3952#ifdef _MSC_VER
3953 #define MA_INLINE __forceinline
3954
3955 /* noinline was introduced in Visual Studio 2005. */
3956 #if _MSC_VER >= 1400
3957 #define MA_NO_INLINE __declspec(noinline)
3958 #else
3959 #define MA_NO_INLINE
3960 #endif
3961#elif defined(__GNUC__)
3962 /*
3963 I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
3964 the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
3965 case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
3966 command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
3967 I am using "__inline__" only when we're compiling in strict ANSI mode.
3968 */
3969 #if defined(__STRICT_ANSI__)
3970 #define MA_GNUC_INLINE_HINT __inline__
3971 #else
3972 #define MA_GNUC_INLINE_HINT inline
3973 #endif
3974
3975 #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
3976 #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline))
3977 #define MA_NO_INLINE __attribute__((noinline))
3978 #else
3979 #define MA_INLINE MA_GNUC_INLINE_HINT
3980 #define MA_NO_INLINE
3981 #endif
3982#elif defined(__WATCOMC__)
3983 #define MA_INLINE __inline
3984 #define MA_NO_INLINE
3985#else
3986 #define MA_INLINE
3987 #define MA_NO_INLINE
3988#endif
3989
3990/* MA_DLL is not officially supported. You're on your own if you want to use this. */
3991#if defined(MA_DLL)
3992 #if defined(_WIN32)
3993 #define MA_DLL_IMPORT __declspec(dllimport)
3994 #define MA_DLL_EXPORT __declspec(dllexport)
3995 #define MA_DLL_PRIVATE static
3996 #else
3997 #if defined(__GNUC__) && __GNUC__ >= 4
3998 #define MA_DLL_IMPORT __attribute__((visibility("default")))
3999 #define MA_DLL_EXPORT __attribute__((visibility("default")))
4000 #define MA_DLL_PRIVATE __attribute__((visibility("hidden")))
4001 #else
4002 #define MA_DLL_IMPORT
4003 #define MA_DLL_EXPORT
4004 #define MA_DLL_PRIVATE static
4005 #endif
4006 #endif
4007#endif
4008
4009#if !defined(MA_API)
4010 #if defined(MA_DLL)
4011 #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
4012 #define MA_API MA_DLL_EXPORT
4013 #else
4014 #define MA_API MA_DLL_IMPORT
4015 #endif
4016 #else
4017 #define MA_API extern
4018 #endif
4019#endif
4020
4021#if !defined(MA_STATIC)
4022 #if defined(MA_DLL)
4023 #define MA_PRIVATE MA_DLL_PRIVATE
4024 #else
4025 #define MA_PRIVATE static
4026 #endif
4027#endif
4028
4029
4030/* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */
4031#define MA_SIMD_ALIGNMENT 32
4032
4033/*
4034Special wchar_t type to ensure any structures in the public sections that reference it have a
4035consistent size across all platforms.
4036
4037On Windows, wchar_t is 2 bytes, whereas everywhere else it's 4 bytes. Since Windows likes to use
4038wchar_t for its IDs, we need a special explicitly sized wchar type that is always 2 bytes on all
4039platforms.
4040*/
4041#if !defined(MA_POSIX) && defined(MA_WIN32)
4042typedef wchar_t ma_wchar_win32;
4043#else
4045#endif
4046
4047
4048
4049/*
4050Logging Levels
4051==============
4052Log levels are only used to give logging callbacks some context as to the severity of a log message
4053so they can do filtering. All log levels will be posted to registered logging callbacks. If you
4054don't want to output a certain log level you can discriminate against the log level in the callback.
4055
4056MA_LOG_LEVEL_DEBUG
4057 Used for debugging. Useful for debug and test builds, but should be disabled in release builds.
4058
4059MA_LOG_LEVEL_INFO
4060 Informational logging. Useful for debugging. This will never be called from within the data
4061 callback.
4062
4063MA_LOG_LEVEL_WARNING
4064 Warnings. You should enable this in you development builds and action them when encountered. These
4065 logs usually indicate a potential problem or misconfiguration, but still allow you to keep
4066 running. This will never be called from within the data callback.
4067
4068MA_LOG_LEVEL_ERROR
4069 Error logging. This will be fired when an operation fails and is subsequently aborted. This can
4070 be fired from within the data callback, in which case the device will be stopped. You should
4071 always have this log level enabled.
4072*/
4080
4081/*
4082Variables needing to be accessed atomically should be declared with this macro for two reasons:
4083
4084 1) It allows people who read the code to identify a variable as such; and
4085 2) It forces alignment on platforms where it's required or optimal.
4086
4087Note that for x86/64, alignment is not strictly necessary, but does have some performance
4088implications. Where supported by the compiler, alignment will be used, but otherwise if the CPU
4089architecture does not require it, it will simply leave it unaligned. This is the case with old
4090versions of Visual Studio, which I've confirmed with at least VC6.
4091*/
4092#if !defined(_MSC_VER) && defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
4093 #include <stdalign.h>
4094 #define MA_ATOMIC(alignment, type) _Alignas(alignment) type
4095#else
4096 #if defined(__GNUC__)
4097 /* GCC-style compilers. */
4098 #define MA_ATOMIC(alignment, type) type __attribute__((aligned(alignment)))
4099 #elif defined(_MSC_VER) && _MSC_VER > 1200 /* 1200 = VC6. Alignment not supported, but not necessary because x86 is the only supported target. */
4100 /* MSVC. */
4101 #define MA_ATOMIC(alignment, type) __declspec(align(alignment)) type
4102 #else
4103 /* Other compilers. */
4104 #define MA_ATOMIC(alignment, type) type
4105 #endif
4106#endif
4107
4108typedef struct ma_context ma_context;
4109typedef struct ma_device ma_device;
4110
4112typedef enum
4113{
4169} _ma_channel_position; /* Do not use `_ma_channel_position` directly. Use `ma_channel` instead. */
4170
4171typedef enum
4172{
4174 MA_ERROR = -1, /* A generic error. */
4192 MA_BUSY = -19,
4226
4227 /* General non-standard errors. */
4229
4230 /* General miniaudio-specific errors. */
4238 MA_LOOP = -207,
4240
4241 /* State errors. */
4246
4247 /* Operation errors. */
4252} ma_result;
4253
4254
4255#define MA_MIN_CHANNELS 1
4256#ifndef MA_MAX_CHANNELS
4257#define MA_MAX_CHANNELS 254
4258#endif
4259
4260#ifndef MA_MAX_FILTER_ORDER
4261#define MA_MAX_FILTER_ORDER 8
4262#endif
4263
4264typedef enum
4265{
4268
4274
4281
4282typedef enum
4283{
4284 /*
4285 I like to keep these explicitly defined because they're used as a key into a lookup table. When items are
4286 added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample().
4287 */
4288 ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */
4290 ma_format_s16 = 2, /* Seems to be the most widely supported format. */
4291 ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */
4295} ma_format;
4296
4297typedef enum
4298{
4299 /* Standard rates need to be in priority order. */
4300 ma_standard_sample_rate_48000 = 48000, /* Most common */
4302
4306
4311
4312 ma_standard_sample_rate_16000 = 16000, /* Extreme lows */
4315
4316 ma_standard_sample_rate_352800 = 352800, /* Extreme highs */
4318
4321 ma_standard_sample_rate_count = 14 /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */
4323
4324
4325typedef enum
4326{
4327 ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */
4328 ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */
4329 ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_converter_config. */
4332
4333typedef enum
4334{
4337 ma_standard_channel_map_rfc3551, /* Based off AIFF. */
4340 ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */
4341 ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */
4342 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. */
4345
4351
4352
4353typedef struct
4354{
4356 void* (* onMalloc)(size_t sz, void* pUserData);
4357 void* (* onRealloc)(void* p, size_t sz, void* pUserData);
4358 void (* onFree)(void* p, void* pUserData);
4360
4361typedef struct
4362{
4364} ma_lcg;
4365
4366
4367/*
4368Atomics.
4369
4370These are typesafe structures to prevent errors as a result of forgetting to reference variables atomically. It's too
4371easy to introduce subtle bugs where you accidentally do a regular assignment instead of an atomic load/store, etc. By
4372using a struct we can enforce the use of atomics at compile time.
4373
4374These types are declared in the header section because we need to reference them in structs below, but functions for
4375using them are only exposed in the implementation section. I do not want these to be part of the public API.
4376
4377There's a few downsides to this system. The first is that you need to declare a new struct for each type. Below are
4378some macros to help with the declarations. They will be named like so:
4379
4380 ma_atomic_uint32 - atomic ma_uint32
4381 ma_atomic_int32 - atomic ma_int32
4382 ma_atomic_uint64 - atomic ma_uint64
4383 ma_atomic_float - atomic float
4384 ma_atomic_bool32 - atomic ma_bool32
4385
4386The other downside is that atomic pointers are extremely messy. You need to declare a new struct for each specific
4387type of pointer you need to make atomic. For example, an atomic ma_node* will look like this:
4388
4389 MA_ATOMIC_SAFE_TYPE_IMPL_PTR(node)
4390
4391Which will declare a type struct that's named like so:
4392
4393 ma_atomic_ptr_node
4394
4395Functions to use the atomic types are declared in the implementation section. All atomic functions are prefixed with
4396the name of the struct. For example:
4397
4398 ma_atomic_uint32_set() - Atomic store of ma_uint32
4399 ma_atomic_uint32_get() - Atomic load of ma_uint32
4400 etc.
4401
4402For pointer types it's the same, which makes them a bit messy to use due to the length of each function name, but in
4403return you get type safety and enforcement of atomic operations.
4404*/
4405#define MA_ATOMIC_SAFE_TYPE_DECL(c89TypeExtension, typeSize, type) \
4406 typedef struct \
4407 { \
4408 MA_ATOMIC(typeSize, ma_##type) value; \
4409 } ma_atomic_##type; \
4410
4411#define MA_ATOMIC_SAFE_TYPE_DECL_PTR(type) \
4412 typedef struct \
4413 { \
4414 MA_ATOMIC(MA_SIZEOF_PTR, ma_##type*) value; \
4415 } ma_atomic_ptr_##type; \
4416
4417MA_ATOMIC_SAFE_TYPE_DECL(32, 4, uint32)
4418MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, int32)
4419MA_ATOMIC_SAFE_TYPE_DECL(64, 8, uint64)
4420MA_ATOMIC_SAFE_TYPE_DECL(f32, 4, float)
4421MA_ATOMIC_SAFE_TYPE_DECL(32, 4, bool32)
4422
4423
4424/* Spinlocks are 32-bit for compatibility reasons. */
4426
4427#ifndef MA_NO_THREADING
4428 /* Thread priorities should be ordered such that the default priority of the worker thread is 0. */
4440
4441 #if defined(MA_POSIX)
4443 #elif defined(MA_WIN32)
4444 typedef ma_handle ma_thread;
4445 #endif
4446
4447 #if defined(MA_POSIX)
4449 #elif defined(MA_WIN32)
4450 typedef ma_handle ma_mutex;
4451 #endif
4452
4453 #if defined(MA_POSIX)
4460 #elif defined(MA_WIN32)
4461 typedef ma_handle ma_event;
4462 #endif
4463
4464 #if defined(MA_POSIX)
4471 #elif defined(MA_WIN32)
4472 typedef ma_handle ma_semaphore;
4473 #endif
4474#else
4475 /* 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. */
4476 #ifndef MA_NO_DEVICE_IO
4477 #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO";
4478 #endif
4479#endif /* MA_NO_THREADING */
4480
4481
4482/*
4483Retrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required.
4484*/
4485MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);
4486
4487/*
4488Retrieves the version of miniaudio as a string which can be useful for logging purposes.
4489*/
4490MA_API const char* ma_version_string(void);
4491
4492
4493/**************************************************************************************************************************************************************
4494
4495Logging
4496
4497**************************************************************************************************************************************************************/
4498#include <stdarg.h> /* For va_list. */
4499
4500#if defined(__has_attribute)
4501 #if __has_attribute(format)
4502 #define MA_ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va)))
4503 #endif
4504#endif
4505#ifndef MA_ATTRIBUTE_FORMAT
4506#define MA_ATTRIBUTE_FORMAT(fmt, va)
4507#endif
4508
4509#ifndef MA_MAX_LOG_CALLBACKS
4510#define MA_MAX_LOG_CALLBACKS 4
4511#endif
4512
4513
4514/*
4515The callback for handling log messages.
4516
4517
4518Parameters
4519----------
4520pUserData (in)
4521 The user data pointer that was passed into ma_log_register_callback().
4522
4523logLevel (in)
4524 The log level. This can be one of the following:
4525
4526 +----------------------+
4527 | Log Level |
4528 +----------------------+
4529 | MA_LOG_LEVEL_DEBUG |
4530 | MA_LOG_LEVEL_INFO |
4531 | MA_LOG_LEVEL_WARNING |
4532 | MA_LOG_LEVEL_ERROR |
4533 +----------------------+
4534
4535pMessage (in)
4536 The log message.
4537*/
4538typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage);
4539
4545
4547
4548
4549typedef struct
4550{
4553 ma_allocation_callbacks allocationCallbacks; /* Need to store these persistently because ma_log_postv() might need to allocate a buffer on the heap. */
4554#ifndef MA_NO_THREADING
4555 ma_mutex lock; /* For thread safety just to make it easier and safer for the logging implementation. */
4556#endif
4557} ma_log;
4558
4559MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog);
4563MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage);
4564MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args);
4565MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) MA_ATTRIBUTE_FORMAT(3, 4);
4566
4567
4568/**************************************************************************************************************************************************************
4569
4570Biquad Filtering
4571
4572**************************************************************************************************************************************************************/
4573typedef union
4574{
4575 float f32;
4578
4579typedef struct
4580{
4583 double b0;
4584 double b1;
4585 double b2;
4586 double a0;
4587 double a1;
4588 double a2;
4590
4591MA_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);
4592
4609
4610MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes);
4612MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ);
4613MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks);
4616MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4618
4619
4620/**************************************************************************************************************************************************************
4621
4622Low-Pass Filtering
4623
4624**************************************************************************************************************************************************************/
4633
4636
4648
4649MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes);
4651MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF);
4652MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);
4655MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4657
4658typedef struct
4659{
4660 ma_biquad bq; /* The second order low-pass filter is implemented as a biquad filter. */
4661} ma_lpf2;
4662
4663MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes);
4665MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF);
4666MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);
4669MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4671
4672
4673typedef struct
4674{
4679 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
4681
4683
4698
4699MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes);
4701MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF);
4702MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);
4705MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4707
4708
4709/**************************************************************************************************************************************************************
4710
4711High-Pass Filtering
4712
4713**************************************************************************************************************************************************************/
4722
4725
4737
4738MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes);
4740MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pHPF);
4741MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks);
4743MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4745
4746typedef struct
4747{
4748 ma_biquad bq; /* The second order high-pass filter is implemented as a biquad filter. */
4749} ma_hpf2;
4750
4751MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes);
4753MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF);
4754MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks);
4756MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4758
4759
4760typedef struct
4761{
4766 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
4768
4770
4785
4786MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes);
4788MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF);
4789MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks);
4791MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4793
4794
4795/**************************************************************************************************************************************************************
4796
4797Band-Pass Filtering
4798
4799**************************************************************************************************************************************************************/
4808
4810
4811typedef struct
4812{
4813 ma_biquad bq; /* The second order band-pass filter is implemented as a biquad filter. */
4814} ma_bpf2;
4815
4816MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes);
4818MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF);
4819MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks);
4821MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4823
4824
4825typedef struct
4826{
4831 ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */
4833
4835
4836typedef struct
4837{
4842
4843 /* Memory management. */
4844 void* _pHeap;
4846} ma_bpf;
4847
4848MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes);
4850MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF);
4851MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks);
4853MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4855
4856
4857/**************************************************************************************************************************************************************
4858
4859Notching Filter
4860
4861**************************************************************************************************************************************************************/
4870
4872
4873typedef struct
4874{
4876} ma_notch2;
4877
4878MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes);
4880MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter);
4881MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);
4883MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4885
4886
4887/**************************************************************************************************************************************************************
4888
4889Peaking EQ Filter
4890
4891**************************************************************************************************************************************************************/
4901
4903
4904typedef struct
4905{
4907} ma_peak2;
4908
4909MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes);
4911MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter);
4912MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);
4914MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4916
4917
4918/**************************************************************************************************************************************************************
4919
4920Low Shelf Filter
4921
4922**************************************************************************************************************************************************************/
4932
4934
4935typedef struct
4936{
4938} ma_loshelf2;
4939
4940MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes);
4942MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter);
4943MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);
4945MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4947
4948
4949/**************************************************************************************************************************************************************
4950
4951High Shelf Filter
4952
4953**************************************************************************************************************************************************************/
4963
4965
4966typedef struct
4967{
4969} ma_hishelf2;
4970
4971MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes);
4973MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter);
4974MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);
4976MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
4978
4979
4980
4981/*
4982Delay
4983*/
4984typedef struct
4985{
4989 ma_bool32 delayStart; /* Set to true to delay the start of the output; false otherwise. */
4990 float wet; /* 0..1. Default = 1. */
4991 float dry; /* 0..1. Default = 1. */
4992 float decay; /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */
4994
4996
4997
4998typedef struct
4999{
5001 ma_uint32 cursor; /* Feedback is written to this cursor. Always equal or in front of the read cursor. */
5003 float* pBuffer;
5004} ma_delay;
5005
5006MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay);
5007MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks);
5008MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount);
5009MA_API void ma_delay_set_wet(ma_delay* pDelay, float value);
5010MA_API float ma_delay_get_wet(const ma_delay* pDelay);
5011MA_API void ma_delay_set_dry(ma_delay* pDelay, float value);
5012MA_API float ma_delay_get_dry(const ma_delay* pDelay);
5013MA_API void ma_delay_set_decay(ma_delay* pDelay, float value);
5015
5016
5017/* Gainer for smooth volume changes. */
5023
5025
5026
5027typedef struct
5028{
5034
5035 /* Memory management. */
5036 void* _pHeap;
5038} ma_gainer;
5039
5040MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes);
5042MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer);
5043MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks);
5044MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
5046MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains);
5049
5050
5051
5052/* Stereo panner. */
5053typedef enum
5054{
5055 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. */
5056 ma_pan_mode_pan /* A true pan. The sound from one side will "move" to the other side and blend with it. */
5057} ma_pan_mode;
5058
5066
5068
5069
5070typedef struct
5071{
5075 float pan; /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */
5076} ma_panner;
5077
5079MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
5082MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan);
5083MA_API float ma_panner_get_pan(const ma_panner* pPanner);
5084
5085
5086
5087/* Fader. */
5094
5096
5097typedef struct
5098{
5100 float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */
5102 ma_uint64 lengthInFrames; /* The total length of the fade. */
5103 ma_int64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). Signed because it'll be offset by startOffsetInFrames in set_fade_ex(). */
5104} ma_fader;
5105
5107MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
5108MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
5109MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames);
5110MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames);
5112
5113
5114
5115/* Spatializer. */
5116typedef struct
5117{
5118 float x;
5119 float y;
5120 float z;
5121} ma_vec3f;
5122
5128
5129typedef enum
5130{
5131 ma_attenuation_model_none, /* No distance attenuation and no spatialization. */
5132 ma_attenuation_model_inverse, /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */
5133 ma_attenuation_model_linear, /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */
5134 ma_attenuation_model_exponential /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */
5136
5142
5148
5149
5150typedef struct
5151{
5154 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. */
5161
5163
5164
5165typedef struct
5166{
5168 ma_atomic_vec3f position; /* The absolute position of the listener. */
5169 ma_atomic_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */
5172
5173 /* Memory management. */
5175 void* _pHeap;
5177
5183MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
5184MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
5185MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z);
5189MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z);
5193MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z);
5197
5198
5199typedef struct
5200{
5206 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. */
5207 float minGain;
5208 float maxGain;
5211 float rolloff;
5215 float dopplerFactor; /* Set to 0 to disable doppler effect. */
5216 float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */
5217 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. */
5218 ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */
5220
5222
5223
5224typedef struct
5225{
5231 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. */
5232 float minGain;
5233 float maxGain;
5236 float rolloff;
5240 float dopplerFactor; /* Set to 0 to disable doppler effect. */
5241 float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */
5242 ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */
5245 ma_atomic_vec3f velocity; /* For doppler effect. */
5246 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. */
5248 ma_gainer gainer; /* For smooth gain transitions. */
5249 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. */
5250
5251 /* Memory management. */
5252 void* _pHeap;
5255
5256MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes);
5258MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer);
5259MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks);
5260MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
5269MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff);
5271MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain);
5273MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain);
5275MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance);
5277MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance);
5279MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
5280MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
5281MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor);
5283MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor);
5285MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z);
5287MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z);
5289MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z);
5291MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir);
5292
5293
5294
5295/************************************************************************************************************************************************************
5296*************************************************************************************************************************************************************
5297
5298DATA CONVERSION
5299===============
5300
5301This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc.
5302
5303*************************************************************************************************************************************************************
5304************************************************************************************************************************************************************/
5305
5306/**************************************************************************************************************************************************************
5307
5308Resampling
5309
5310**************************************************************************************************************************************************************/
5311typedef struct
5312{
5317 ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */
5318 double lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */
5320
5322
5323typedef struct
5324{
5330 union
5331 {
5332 float* f32;
5334 } x0; /* The previous input frame. */
5335 union
5336 {
5337 float* f32;
5338 ma_int16* s16;
5339 } x1; /* The next input frame. */
5341
5342 /* Memory management. */
5343 void* _pHeap;
5346
5351MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
5359
5360
5362
5364typedef struct
5365{
5366 ma_result (* onGetHeapSize )(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes);
5367 ma_result (* onInit )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend);
5368 void (* onUninit )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks);
5369 ma_result (* onProcess )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
5370 ma_result (* onSetRate )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); /* Optional. Rate changes will be disabled. */
5371 ma_uint64 (* onGetInputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */
5372 ma_uint64 (* onGetOutputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */
5373 ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); /* Optional. Latency mitigation will be disabled. */
5374 ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* Optional. Latency mitigation will be disabled. */
5375 ma_result (* onReset )(void* pUserData, ma_resampling_backend* pBackend);
5377
5378typedef enum
5379{
5380 ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */
5383
5385{
5386 ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */
5390 ma_resample_algorithm algorithm; /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */
5393 struct
5394 {
5397};
5398
5400
5401typedef struct
5402{
5410 union
5411 {
5413 } state; /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */
5414
5415 /* Memory management. */
5416 void* _pHeap;
5418} ma_resampler;
5419
5420MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes);
5422
5423/*
5424Initializes a new resampler object from a config.
5425*/
5426MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler);
5427
5428/*
5429Uninitializes a resampler.
5430*/
5431MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks);
5432
5433/*
5434Converts the given input data.
5435
5436Both the input and output frames must be in the format specified in the config when the resampler was initialized.
5437
5438On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that
5439were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use
5440ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames.
5441
5442On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole
5443input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames
5444you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead.
5445
5446If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of
5447output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input
5448frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be
5449processed. In this case, any internal filter state will be updated as if zeroes were passed in.
5450
5451It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL.
5452
5453It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL.
5454*/
5455MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
5456
5457
5458/*
5459Sets the input and output sample rate.
5460*/
5461MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
5462
5463/*
5464Sets the input and output sample rate as a ratio.
5465
5466The ration is in/out.
5467*/
5469
5470/*
5471Retrieves the latency introduced by the resampler in input frames.
5472*/
5474
5475/*
5476Retrieves the latency introduced by the resampler in output frames.
5477*/
5479
5480/*
5481Calculates the number of whole input frames that would need to be read from the client in order to output the specified
5482number of output frames.
5483
5484The returned value does not include cached input frames. It only returns the number of extra frames that would need to be
5485read from the input buffer in order to output the specified number of output frames.
5486*/
5487MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);
5488
5489/*
5490Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of
5491input frames.
5492*/
5494
5495/*
5496Resets the resampler's timer and clears its internal cache.
5497*/
5499
5500
5501/**************************************************************************************************************************************************************
5502
5503Channel Conversion
5504
5505**************************************************************************************************************************************************************/
5506typedef enum
5507{
5510 ma_channel_conversion_path_mono_out, /* Converting to mono. */
5511 ma_channel_conversion_path_mono_in, /* Converting from mono. */
5512 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. */
5513 ma_channel_conversion_path_weights /* Blended based on weights. */
5515
5516typedef enum
5517{
5518 ma_mono_expansion_mode_duplicate = 0, /* The default. */
5519 ma_mono_expansion_mode_average, /* Average the mono channel across all channels. */
5520 ma_mono_expansion_mode_stereo_only, /* Duplicate to the left and right channels only and ignore the others. */
5523
5524typedef struct
5525{
5532 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. */
5533 float** ppWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
5535
5536MA_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);
5537
5558
5563MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
5566
5567
5568/**************************************************************************************************************************************************************
5569
5570Data Conversion
5571
5572**************************************************************************************************************************************************************/
5573typedef struct
5574{
5585 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. */
5586 float** ppChannelWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
5590
5592MA_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);
5593
5594
5595typedef enum
5596{
5601 ma_data_converter_execution_path_resample_first, /* All conversions, but resample as the first step. */
5602 ma_data_converter_execution_path_channels_first /* All conversions, but channels as the first step. */
5604
5627
5631MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks);
5632MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
5639MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);
5640MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);
5642
5643
5644/************************************************************************************************************************************************************
5645
5646Format Conversion
5647
5648************************************************************************************************************************************************************/
5649MA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5650MA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5651MA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5652MA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5653MA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5654MA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5655MA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5656MA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5657MA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5658MA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5659MA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5660MA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5661MA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5662MA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5663MA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5664MA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5665MA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5666MA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5667MA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5668MA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
5669MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode);
5670MA_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);
5671
5672/*
5673Deinterleaves an interleaved buffer.
5674*/
5675MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);
5676
5677/*
5678Interleaves a group of deinterleaved buffers.
5679*/
5680MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);
5681
5682
5683/************************************************************************************************************************************************************
5684
5685Channel Maps
5686
5687************************************************************************************************************************************************************/
5688/*
5689This is used in the shuffle table to indicate that the channel index is undefined and should be ignored.
5690*/
5691#define MA_CHANNEL_INDEX_NULL 255
5692
5693/*
5694Retrieves the channel position of the specified channel in the given channel map.
5695
5696The pChannelMap parameter can be null, in which case miniaudio's default channel map will be assumed.
5697*/
5698MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex);
5699
5700/*
5701Initializes a blank channel map.
5702
5703When a blank channel map is specified anywhere it indicates that the native channel map should be used.
5704*/
5706
5707/*
5708Helper for retrieving a standard channel map.
5709
5710The output channel map buffer must have a capacity of at least `channelMapCap`.
5711*/
5712MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels);
5713
5714/*
5715Copies a channel map.
5716
5717Both input and output channel map buffers must have a capacity of at least `channels`.
5718*/
5720
5721/*
5722Copies a channel map if one is specified, otherwise copies the default channel map.
5723
5724The 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`.
5725*/
5726MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels);
5727
5728
5729/*
5730Determines whether or not a channel map is valid.
5731
5732A blank channel map is valid (all channels set to MA_CHANNEL_NONE). The way a blank channel map is handled is context specific, but
5733is usually treated as a passthrough.
5734
5735Invalid channel maps:
5736 - A channel map with no channels
5737 - A channel map with more than one channel and a mono channel
5738
5739The channel map buffer must have a capacity of at least `channels`.
5740*/
5742
5743/*
5744Helper for comparing two channel maps for equality.
5745
5746This assumes the channel count is the same between the two.
5747
5748Both channels map buffers must have a capacity of at least `channels`.
5749*/
5750MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels);
5751
5752/*
5753Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE).
5754
5755The channel map buffer must have a capacity of at least `channels`.
5756*/
5758
5759/*
5760Helper for determining whether or not a channel is present in the given channel map.
5761
5762The channel map buffer must have a capacity of at least `channels`.
5763*/
5765
5766/*
5767Find a channel position in the given channel map. Returns MA_TRUE if the channel is found; MA_FALSE otherwise. The
5768index of the channel is output to `pChannelIndex`.
5769
5770The channel map buffer must have a capacity of at least `channels`.
5771*/
5772MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex);
5773
5774/*
5775Generates a string representing the given channel map.
5776
5777This is for printing and debugging purposes, not serialization/deserialization.
5778
5779Returns the length of the string, not including the null terminator.
5780*/
5781MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap);
5782
5783/*
5784Retrieves a human readable version of a channel position.
5785*/
5787
5788
5789/************************************************************************************************************************************************************
5790
5791Conversion Helpers
5792
5793************************************************************************************************************************************************************/
5794
5795/*
5796High-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
5797determine the required size of the output buffer. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is
5798ignored.
5799
5800A return value of 0 indicates an error.
5801
5802This function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_data_converter APIs instead.
5803*/
5804MA_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);
5805MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig);
5806
5807
5808/************************************************************************************************************************************************************
5809
5810Data Source
5811
5812************************************************************************************************************************************************************/
5813typedef void ma_data_source;
5814
5815#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001
5816
5817typedef struct
5818{
5819 ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
5820 ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex);
5821 ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
5822 ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor);
5823 ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength);
5824 ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping);
5827
5828typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource);
5829
5830typedef struct
5831{
5834
5836
5837
5838typedef struct
5839{
5842 ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */
5843 ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */
5844 ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */
5845 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. */
5846 ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */
5847 ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */
5848 MA_ATOMIC(4, ma_bool32) isLooping;
5850
5853MA_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. */
5854MA_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); */
5856MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked); /* Can only seek forward. Abstraction to ma_data_source_seek_pcm_frames() */
5857MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds); /* Abstraction to ma_data_source_seek_to_pcm_frame() */
5858MA_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);
5860MA_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. */
5866MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames);
5868MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames);
5875
5876
5887
5888MA_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);
5890MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames);
5893MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount);
5894MA_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. */
5899
5900
5901
5902typedef struct
5903{
5908 const void* pData; /* If set to NULL, will allocate a block of memory for you. */
5911
5912MA_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);
5913
5914typedef struct
5915{
5918 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(). */
5919 ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */
5921
5924MA_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. */
5927MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop);
5929MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount);
5930MA_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. */
5935
5936
5937/*
5938Paged Audio Buffer
5939==================
5940A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It
5941can be used for cases where audio data is streamed in asynchronously while allowing data to be read
5942at the same time.
5943
5944This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across
5945simultaneously across different threads, however only one thread at a time can append, and only one
5946thread at a time can read and seek.
5947*/
5955
5956typedef struct
5957{
5960 ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */
5961 MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */
5963
5969MA_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);
5972MA_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);
5973
5974
5975typedef struct
5976{
5977 ma_paged_audio_buffer_data* pData; /* Must not be null. */
5979
5981
5982
5983typedef struct
5984{
5986 ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */
5988 ma_uint64 relativeCursor; /* Relative to the current page. */
5991
5994MA_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. */
5998
5999
6000
6001/************************************************************************************************************************************************************
6002
6003Ring Buffer
6004
6005************************************************************************************************************************************************************/
6006typedef struct
6007{
6008 void* pBuffer;
6012 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. */
6013 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. */
6014 ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */
6015 ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */
6017} ma_rb;
6018
6019MA_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);
6020MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB);
6023MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
6024MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes);
6025MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
6026MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes);
6027MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes);
6028MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes);
6029MA_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. */
6034MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex);
6035MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer);
6036
6037
6038typedef struct
6039{
6044 ma_uint32 sampleRate; /* Not required for the ring buffer itself, but useful for associating the data with some sample rate, particularly for data sources. */
6045} ma_pcm_rb;
6046
6047MA_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);
6048MA_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);
6051MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
6053MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
6057MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */
6063MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer);
6068
6069
6070/*
6071The idea of the duplex ring buffer is to act as the intermediary buffer when running two asynchronous devices in a duplex set up. The
6072capture device writes to it, and then a playback device reads from it.
6073
6074At the moment this is just a simple naive implementation, but in the future I want to implement some dynamic resampling to seamlessly
6075handle desyncs. Note that the API is work in progress and may change at any time in any version.
6076
6077The 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
6078in frames. The internal sample rate of the capture device is also needed in order to calculate the size.
6079*/
6080typedef struct
6081{
6083} ma_duplex_rb;
6084
6085MA_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);
6087
6088
6089/************************************************************************************************************************************************************
6090
6091Miscellaneous Helpers
6092
6093************************************************************************************************************************************************************/
6094/*
6095Retrieves a human readable description of the given result code.
6096*/
6098
6099/*
6100malloc()
6101*/
6102MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
6103
6104/*
6105calloc()
6106*/
6107MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
6108
6109/*
6110realloc()
6111*/
6112MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
6113
6114/*
6115free()
6116*/
6117MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
6118
6119/*
6120Performs an aligned malloc, with the assumption that the alignment is a power of 2.
6121*/
6122MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks);
6123
6124/*
6125Free's an aligned malloc'd buffer.
6126*/
6127MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
6128
6129/*
6130Retrieves a friendly name for a format.
6131*/
6133
6134/*
6135Blends two frames in floating point format.
6136*/
6137MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels);
6138
6139/*
6140Retrieves the size of a sample in bytes for the given format.
6141
6142This API is efficient and is implemented using a lookup table.
6143
6144Thread Safety: SAFE
6145 This API is pure.
6146*/
6148static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; }
6149
6150/*
6151Converts a log level to a string.
6152*/
6154
6155
6156
6157
6158/************************************************************************************************************************************************************
6159
6160Synchronization
6161
6162************************************************************************************************************************************************************/
6163/*
6164Locks a spinlock.
6165*/
6167
6168/*
6169Locks a spinlock, but does not yield() when looping.
6170*/
6172
6173/*
6174Unlocks a spinlock.
6175*/
6177
6178
6179#ifndef MA_NO_THREADING
6180
6181/*
6182Creates a mutex.
6183
6184A mutex must be created from a valid context. A mutex is initially unlocked.
6185*/
6187
6188/*
6189Deletes a mutex.
6190*/
6192
6193/*
6194Locks a mutex with an infinite timeout.
6195*/
6197
6198/*
6199Unlocks a mutex.
6200*/
6202
6203
6204/*
6205Initializes an auto-reset event.
6206*/
6208
6209/*
6210Uninitializes an auto-reset event.
6211*/
6213
6214/*
6215Waits for the specified auto-reset event to become signalled.
6216*/
6218
6219/*
6220Signals the specified auto-reset event.
6221*/
6223
6224
6225MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore);
6229#endif /* MA_NO_THREADING */
6230
6231
6232/*
6233Fence
6234=====
6235This locks while the counter is larger than 0. Counter can be incremented and decremented by any
6236thread, but care needs to be taken when waiting. It is possible for one thread to acquire the
6237fence just as another thread returns from ma_fence_wait().
6238
6239The idea behind a fence is to allow you to wait for a group of operations to complete. When an
6240operation starts, the counter is incremented which locks the fence. When the operation completes,
6241the fence will be released which decrements the counter. ma_fence_wait() will block until the
6242counter hits zero.
6243
6244If threading is disabled, ma_fence_wait() will spin on the counter.
6245*/
6246typedef struct
6247{
6248#ifndef MA_NO_THREADING
6250#endif
6252} ma_fence;
6253
6256MA_API ma_result ma_fence_acquire(ma_fence* pFence); /* Increment counter. */
6257MA_API ma_result ma_fence_release(ma_fence* pFence); /* Decrement counter. */
6258MA_API ma_result ma_fence_wait(ma_fence* pFence); /* Wait for counter to reach 0. */
6259
6260
6261
6262/*
6263Notification callback for asynchronous operations.
6264*/
6266
6267typedef struct
6268{
6269 void (* onSignal)(ma_async_notification* pNotification);
6271
6273
6274
6275/*
6276Simple polling notification.
6277
6278This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled()
6279*/
6285
6288
6289
6290/*
6291Event Notification
6292
6293This uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail.
6294*/
6295typedef struct
6296{
6298#ifndef MA_NO_THREADING
6300#endif
6302
6307
6308
6309
6310
6311/************************************************************************************************************************************************************
6312
6313Job Queue
6314
6315************************************************************************************************************************************************************/
6316
6317/*
6318Slot Allocator
6319--------------
6320The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocate an index that can be used
6321as the insertion point for an object.
6322
6323Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs.
6324
6325The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits:
6326
6327 +-----------------+-----------------+
6328 | 32 Bits | 32 Bits |
6329 +-----------------+-----------------+
6330 | Reference Count | Slot Index |
6331 +-----------------+-----------------+
6332*/
6333typedef struct
6334{
6335 ma_uint32 capacity; /* The number of slots to make available. */
6337
6339
6340
6341typedef struct
6342{
6343 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. */
6345
6346typedef struct
6347{
6348 ma_slot_allocator_group* pGroups; /* Slots are grouped in chunks of 32. */
6349 ma_uint32* pSlots; /* 32 bits for reference counting for ABA mitigation. */
6350 ma_uint32 count; /* Allocation count. */
6352
6353 /* Memory management. */
6355 void* _pHeap;
6357
6361MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks);
6364
6365
6366typedef struct ma_job ma_job;
6367
6368/*
6369Callback for processing a job. Each job type will have their own processing callback which will be
6370called by ma_job_process().
6371*/
6372typedef ma_result (* ma_job_proc)(ma_job* pJob);
6373
6374/* When a job type is added here an callback needs to be added go "g_jobVTable" in the implementation section. */
6398
6400{
6401 union
6402 {
6403 struct
6404 {
6405 ma_uint16 code; /* Job type. */
6406 ma_uint16 slot; /* Index into a ma_slot_allocator. */
6410 } toc; /* 8 bytes. We encode the job code into the slot allocation data to save space. */
6411 MA_ATOMIC(8, ma_uint64) next; /* refcount + slot for the next item. Does not include the job code. */
6412 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. */
6413
6414 union
6415 {
6416 /* Miscellaneous. */
6417 struct
6418 {
6423
6424 /* Resource Manager */
6425 union
6426 {
6427 struct
6428 {
6429 /*ma_resource_manager**/ void* pResourceManager;
6430 /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode;
6432 wchar_t* pFilePathW;
6433 ma_uint32 flags; /* Resource manager data source flags that were used when initializing the data buffer. */
6434 ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */
6435 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. */
6436 ma_fence* pInitFence; /* Released when initialization of the decoder is complete. */
6437 ma_fence* pDoneFence; /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */
6439 struct
6440 {
6441 /*ma_resource_manager**/ void* pResourceManager;
6442 /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode;
6446 struct
6447 {
6448 /*ma_resource_manager**/ void* pResourceManager;
6449 /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode;
6450 /*ma_decoder**/ void* pDecoder;
6451 ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */
6452 ma_fence* pDoneFence; /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */
6454
6455 struct
6456 {
6457 /*ma_resource_manager_data_buffer**/ void* pDataBuffer;
6458 ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */
6459 ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */
6460 ma_fence* pInitFence; /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */
6461 ma_fence* pDoneFence; /* Released when the data buffer has been fully decoded. */
6468 struct
6469 {
6470 /*ma_resource_manager_data_buffer**/ void* pDataBuffer;
6474
6475 struct
6476 {
6477 /*ma_resource_manager_data_stream**/ void* pDataStream;
6478 char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */
6479 wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */
6481 ma_async_notification* pInitNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */
6484 struct
6485 {
6486 /*ma_resource_manager_data_stream**/ void* pDataStream;
6490 struct
6491 {
6492 /*ma_resource_manager_data_stream**/ void* pDataStream;
6493 ma_uint32 pageIndex; /* The index of the page to decode into. */
6495 struct
6496 {
6497 /*ma_resource_manager_data_stream**/ void* pDataStream;
6501
6502 /* Device. */
6503 union
6504 {
6505 union
6506 {
6507 struct
6508 {
6509 /*ma_device**/ void* pDevice;
6510 /*ma_device_type*/ ma_uint32 deviceType;
6515};
6516
6519
6520
6521/*
6522When set, ma_job_queue_next() will not wait and no semaphore will be signaled in
6523ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available.
6524
6525This flag should always be used for platforms that do not support multithreading.
6526*/
6527typedef enum
6528{
6531
6532typedef struct
6533{
6535 ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. */
6537
6539
6540
6541typedef struct
6542{
6543 ma_uint32 flags; /* Flags passed in at initialization time. */
6544 ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */
6545 MA_ATOMIC(8, ma_uint64) head; /* The first item in the list. Required for removing from the top of the list. */
6546 MA_ATOMIC(8, ma_uint64) tail; /* The last item in the list. Required for appending to the end of the list. */
6547#ifndef MA_NO_THREADING
6548 ma_semaphore sem; /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */
6549#endif
6552#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
6554#endif
6555
6556 /* Memory management. */
6557 void* _pHeap;
6559} ma_job_queue;
6560
6561MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes);
6564MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks);
6566MA_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. */
6567
6568
6569
6570/************************************************************************************************************************************************************
6571*************************************************************************************************************************************************************
6572
6573DEVICE I/O
6574==========
6575
6576This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc.
6577
6578*************************************************************************************************************************************************************
6579************************************************************************************************************************************************************/
6580#ifndef MA_NO_DEVICE_IO
6581/* Some backends are only supported on certain platforms. */
6582#if defined(MA_WIN32)
6583 #define MA_SUPPORT_WASAPI
6584
6585 #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */
6586 #define MA_SUPPORT_DSOUND
6587 #define MA_SUPPORT_WINMM
6588
6589 /* Don't enable JACK here if compiling with Cosmopolitan. It'll be enabled in the Linux section below. */
6590 #if !defined(__COSMOPOLITAN__)
6591 #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */
6592 #endif
6593 #endif
6594#endif
6595#if defined(MA_UNIX) && !defined(MA_ORBIS) && !defined(MA_PROSPERO)
6596 #if defined(MA_LINUX)
6597 #if !defined(MA_ANDROID) && !defined(__COSMOPOLITAN__) /* ALSA is not supported on Android. */
6598 #define MA_SUPPORT_ALSA
6599 #endif
6600 #endif
6601 #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN)
6602 #define MA_SUPPORT_PULSEAUDIO
6603 #define MA_SUPPORT_JACK
6604 #endif
6605 #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */
6606 #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */
6607 #endif
6608 #if defined(__NetBSD__) || defined(__OpenBSD__)
6609 #define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */
6610 #endif
6611 #if defined(__FreeBSD__) || defined(__DragonFly__)
6612 #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */
6613 #endif
6614#endif
6615#if defined(MA_ANDROID)
6616 #define MA_SUPPORT_AAUDIO
6617 #define MA_SUPPORT_OPENSL
6618#endif
6619#if defined(MA_APPLE)
6620 #define MA_SUPPORT_COREAUDIO
6621#endif
6622#if defined(MA_EMSCRIPTEN)
6623 #define MA_SUPPORT_WEBAUDIO
6624#endif
6625
6626/* All platforms should support custom backends. */
6627#define MA_SUPPORT_CUSTOM
6628
6629/* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */
6630#if !defined(MA_EMSCRIPTEN)
6631#define MA_SUPPORT_NULL
6632#endif
6633
6634
6635#if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI))
6636 #define MA_HAS_WASAPI
6637#endif
6638#if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND))
6639 #define MA_HAS_DSOUND
6640#endif
6641#if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM))
6642 #define MA_HAS_WINMM
6643#endif
6644#if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA))
6645 #define MA_HAS_ALSA
6646#endif
6647#if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO))
6648 #define MA_HAS_PULSEAUDIO
6649#endif
6650#if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK))
6651 #define MA_HAS_JACK
6652#endif
6653#if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO))
6654 #define MA_HAS_COREAUDIO
6655#endif
6656#if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO))
6657 #define MA_HAS_SNDIO
6658#endif
6659#if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4))
6660 #define MA_HAS_AUDIO4
6661#endif
6662#if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS))
6663 #define MA_HAS_OSS
6664#endif
6665#if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO))
6666 #define MA_HAS_AAUDIO
6667#endif
6668#if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL))
6669 #define MA_HAS_OPENSL
6670#endif
6671#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO))
6672 #define MA_HAS_WEBAUDIO
6673#endif
6674#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM))
6675 #define MA_HAS_CUSTOM
6676#endif
6677#if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL))
6678 #define MA_HAS_NULL
6679#endif
6680
6681typedef enum
6682{
6684 ma_device_state_stopped = 1, /* The device's default state after initialization. */
6685 ma_device_state_started = 2, /* The device is started and is requesting and/or delivering audio data. */
6686 ma_device_state_starting = 3, /* Transitioning from a stopped state to started. */
6687 ma_device_state_stopping = 4 /* Transitioning from a started state to stopped. */
6689
6690MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, device_state)
6691
6692
6693#ifdef MA_SUPPORT_WASAPI
6694/* We need a IMMNotificationClient object for WASAPI. */
6695typedef struct
6696{
6697 void* lpVtbl;
6698 ma_uint32 counter;
6699 ma_device* pDevice;
6700} ma_IMMNotificationClient;
6701#endif
6702
6703/* Backend enums must be in priority order. */
6704typedef enum
6705{
6719 ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */
6720 ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */
6721} ma_backend;
6722
6723#define MA_BACKEND_COUNT (ma_backend_null+1)
6724
6725
6726/*
6727Device job thread. This is used by backends that require asynchronous processing of certain
6728operations. It is not used by all backends.
6729
6730The device job thread is made up of a thread and a job queue. You can post a job to the thread with
6731ma_device_job_thread_post(). The thread will do the processing of the job.
6732*/
6733typedef struct
6734{
6735 ma_bool32 noThread; /* Set this to true if you want to process jobs yourself. */
6739
6741
6748
6753
6754
6755
6756/* Device notification types. */
6766
6767typedef struct
6768{
6771 union
6772 {
6773 struct
6774 {
6776 } started;
6777 struct
6778 {
6779 int _unused;
6780 } stopped;
6781 struct
6782 {
6783 int _unused;
6784 } rerouted;
6785 struct
6786 {
6787 int _unused;
6788 } interruption;
6789 } data;
6791
6792/*
6793The notification callback for when the application should be notified of a change to the device.
6794
6795This callback is used for notifying the application of changes such as when the device has started,
6796stopped, rerouted or an interruption has occurred. Note that not all backends will post all
6797notification types. For example, some backends will perform automatic stream routing without any
6798kind of notification to the host program which means miniaudio will never know about it and will
6799never be able to fire the rerouted notification. You should keep this in mind when designing your
6800program.
6801
6802The stopped notification will *not* get fired when a device is rerouted.
6803
6804
6805Parameters
6806----------
6807pNotification (in)
6808 A pointer to a structure containing information about the event. Use the `pDevice` member of
6809 this object to retrieve the relevant device. The `type` member can be used to discriminate
6810 against each of the notification types.
6811
6812
6813Remarks
6814-------
6815Do not restart or uninitialize the device from the callback.
6816
6817Not all notifications will be triggered by all backends, however the started and stopped events
6818should be reliable for all backends. Some backends do not have a good way to detect device
6819stoppages due to unplugging the device which may result in the stopped callback not getting
6820fired. This has been observed with at least one BSD variant.
6821
6822The rerouted notification is fired *after* the reroute has occurred. The stopped notification will
6823*not* get fired when a device is rerouted. The following backends are known to do automatic stream
6824rerouting, but do not have a way to be notified of the change:
6825
6826 * DirectSound
6827
6828The interruption notifications are used on mobile platforms for detecting when audio is interrupted
6829due to things like an incoming phone call. Currently this is only implemented on iOS. None of the
6830Android backends will report this notification.
6831*/
6832typedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification);
6833
6834
6835/*
6836The callback for processing audio data from the device.
6837
6838The 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
6839available. 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
6840callback will be fired with a consistent frame count.
6841
6842
6843Parameters
6844----------
6845pDevice (in)
6846 A pointer to the relevant device.
6847
6848pOutput (out)
6849 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
6850 full-duplex device and null for a capture and loopback device.
6851
6852pInput (in)
6853 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
6854 playback device.
6855
6856frameCount (in)
6857 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
6858 `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must
6859 not assume this will always be the same value each time the callback is fired.
6860
6861
6862Remarks
6863-------
6864You 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
6865callback. The following APIs cannot be called from inside the callback:
6866
6867 ma_device_init()
6868 ma_device_init_ex()
6869 ma_device_uninit()
6870 ma_device_start()
6871 ma_device_stop()
6872
6873The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread.
6874*/
6875typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
6876
6877
6878
6879
6880/*
6881DEPRECATED. Use ma_device_notification_proc instead.
6882
6883The callback for when the device has been stopped.
6884
6885This 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
6886such as being unplugged or an internal error occurring.
6887
6888
6889Parameters
6890----------
6891pDevice (in)
6892 A pointer to the device that has just stopped.
6893
6894
6895Remarks
6896-------
6897Do not restart or uninitialize the device from the callback.
6898*/
6899typedef void (* ma_stop_proc)(ma_device* pDevice); /* DEPRECATED. Use ma_device_notification_proc instead. */
6900
6908
6914
6915/* iOS/tvOS/watchOS session categories. */
6916typedef enum
6917{
6918 ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord. */
6919 ma_ios_session_category_none, /* Leave the session category unchanged. */
6920 ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */
6921 ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */
6922 ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */
6923 ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */
6924 ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */
6925 ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */
6927
6928/* iOS/tvOS/watchOS session category options */
6929typedef enum
6930{
6931 ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */
6932 ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */
6933 ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */
6934 ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */
6935 ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */
6936 ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */
6937 ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */
6939
6940/* OpenSL stream types. */
6941typedef enum
6942{
6943 ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */
6944 ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */
6945 ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */
6946 ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */
6947 ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */
6948 ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */
6949 ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */
6951
6952/* OpenSL recording presets. */
6953typedef enum
6954{
6955 ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */
6956 ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */
6957 ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */
6958 ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */
6959 ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */
6960 ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */
6962
6963/* WASAPI audio thread priority characteristics. */
6970
6971/* AAudio usage types. */
6972typedef enum
6973{
6974 ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */
6975 ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */
6976 ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */
6977 ma_aaudio_usage_voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */
6978 ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */
6979 ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */
6980 ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */
6981 ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */
6982 ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */
6983 ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */
6984 ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */
6985 ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */
6986 ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */
6987 ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */
6988 ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */
6989 ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */
6990 ma_aaudio_usage_announcement /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */
6992
6993/* AAudio content types. */
6994typedef enum
6995{
6996 ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */
6997 ma_aaudio_content_type_speech, /* AAUDIO_CONTENT_TYPE_SPEECH */
6998 ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */
6999 ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */
7000 ma_aaudio_content_type_sonification /* AAUDIO_CONTENT_TYPE_SONIFICATION */
7002
7003/* AAudio input presets. */
7004typedef enum
7005{
7006 ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */
7007 ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */
7008 ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */
7009 ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */
7010 ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */
7011 ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */
7012 ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */
7014
7015typedef enum
7016{
7017 ma_aaudio_allow_capture_default = 0, /* Leaves the allowed capture policy unset. */
7018 ma_aaudio_allow_capture_by_all, /* AAUDIO_ALLOW_CAPTURE_BY_ALL */
7019 ma_aaudio_allow_capture_by_system, /* AAUDIO_ALLOW_CAPTURE_BY_SYSTEM */
7020 ma_aaudio_allow_capture_by_none /* AAUDIO_ALLOW_CAPTURE_BY_NONE */
7022
7023typedef union
7024{
7026 double counterD;
7027} ma_timer;
7028
7029typedef union
7030{
7031 ma_wchar_win32 wasapi[64]; /* WASAPI uses a wchar_t string for identification. */
7032 ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */
7033 /*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. */
7034 char alsa[256]; /* ALSA uses a name string for identification. */
7035 char pulse[256]; /* PulseAudio uses a name string for identification. */
7036 int jack; /* JACK always uses default devices. */
7037 char coreaudio[256]; /* Core Audio uses a string for identification. */
7038 char sndio[256]; /* "snd/0", etc. */
7039 char audio4[256]; /* "/dev/audio", etc. */
7040 char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */
7041 ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */
7042 ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */
7043 char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */
7044 union
7045 {
7046 int i;
7047 char s[256];
7048 void* p;
7049 } custom; /* The custom backend could be anything. Give them a few options. */
7050 int nullbackend; /* The null backend uses an integer for device IDs. */
7051} ma_device_id;
7052
7054
7055
7059
7060#define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */
7061
7062#ifndef MA_MAX_DEVICE_NAME_LENGTH
7063#define MA_MAX_DEVICE_NAME_LENGTH 255
7064#endif
7065
7066typedef struct
7067{
7068 /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */
7070 char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* +1 for null terminator. */
7072
7074 struct
7075 {
7076 ma_format format; /* Sample format. If set to ma_format_unknown, all sample formats are supported. */
7077 ma_uint32 channels; /* If set to 0, all channels are supported. */
7078 ma_uint32 sampleRate; /* If set to 0, all sample rates are supported. */
7079 ma_uint32 flags; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */
7080 } 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. */
7082
7084{
7091 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. */
7092 ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will not be clipped after returning. Only applies when the playback sample format is f32. */
7093 ma_bool8 noDisableDenormals; /* Do not disable denormals when firing the data callback. */
7094 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. */
7100 struct
7101 {
7107 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. */
7110 struct
7111 {
7112 const ma_device_id* pDeviceID;
7117 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. */
7120
7121 struct
7122 {
7123 ma_wasapi_usage usage; /* When configured, uses Avrt APIs to set the thread characteristics. */
7124 ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
7125 ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
7126 ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */
7127 ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */
7128 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. */
7129 ma_bool8 loopbackProcessExclude; /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */
7131 struct
7132 {
7133 ma_bool32 noMMap; /* Disables MMap mode. */
7134 ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */
7135 ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */
7136 ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */
7138 struct
7139 {
7144 struct
7145 {
7146 ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */
7148 struct
7149 {
7154 struct
7155 {
7164};
7165
7166
7167/*
7168The callback for handling device enumeration. This is fired from `ma_context_enumerate_devices()`.
7169
7170
7171Parameters
7172----------
7173pContext (in)
7174 A pointer to the context performing the enumeration.
7175
7176deviceType (in)
7177 The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`.
7178
7179pInfo (in)
7180 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,
7181 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
7182 is too inefficient.
7183
7184pUserData (in)
7185 The user data pointer passed into `ma_context_enumerate_devices()`.
7186*/
7187typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData);
7188
7189
7190/*
7191Describes some basic details about a playback or capture device.
7192*/
7205
7206/*
7207These are the callbacks required to be implemented for a backend. These callbacks are grouped into two parts: context and device. There is one context
7208to many devices. A device is created from a context.
7209
7210The general flow goes like this:
7211
7212 1) A context is created with `onContextInit()`
7213 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required.
7214 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required.
7215 2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was
7216 selected from device enumeration via `onContextEnumerateDevices()`.
7217 3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()`
7218 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
7219 to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by
7220 miniaudio internally.
7221
7222Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the
7223callbacks defined in this structure.
7224
7225Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which
7226physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the
7227given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration
7228needs to stop and the `onContextEnumerateDevices()` function returns with a success code.
7229
7230Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID,
7231and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the
7232case when the device ID is NULL, in which case information about the default device needs to be retrieved.
7233
7234Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created.
7235This is a little bit more complicated than initialization of the context due to its more complicated configuration. When initializing a
7236device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input,
7237the 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
7238the requested format. The conversion between the format requested by the application and the device's native format will be handled
7239internally by miniaudio.
7240
7241On 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
7242supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for
7243sample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to
7244`MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should
7245inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period
7246size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the
7247sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_descriptor`
7248object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set).
7249
7250Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses
7251asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented.
7252
7253The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit
7254easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and
7255`onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the
7256backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within its callback.
7257This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback.
7258
7259If the backend requires absolute flexibility with its data delivery, it can optionally implement the `onDeviceDataLoop()` callback
7260which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional.
7261
7262The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been
7263encountered. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback.
7264
7265The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this
7266callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated
7267which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback,
7268look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to
7269wake up the audio thread.
7270
7271If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the
7272`onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient.
7273*/
7275{
7276 ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks);
7279 ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo);
7280 ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture);
7284 ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead);
7285 ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten);
7289};
7290
7292{
7298 struct
7299 {
7300 ma_handle hWnd; /* HWND. Optional window handle to pass into SetCooperativeLevel(). Will default to the foreground window, and if that fails, the desktop window. */
7302 struct
7303 {
7306 struct
7307 {
7308 const char* pApplicationName;
7309 const char* pServerName;
7310 ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */
7312 struct
7313 {
7316 ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */
7317 ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */
7319 struct
7320 {
7321 const char* pClientName;
7325};
7326
7327/* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */
7328typedef struct
7329{
7330 int code;
7331 ma_event* pEvent; /* This will be signalled when the event is complete. */
7332 union
7333 {
7334 struct
7335 {
7337 } quit;
7338 struct
7339 {
7343 ma_result* pResult; /* The result from creating the audio client service. */
7344 } createAudioClient;
7345 struct
7346 {
7349 } releaseAudioClient;
7350 } data;
7352
7354{
7356 ma_backend backend; /* DirectSound, ALSA, etc. */
7358 ma_log log; /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */
7363 ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */
7364 ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */
7365 ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */
7368 ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */
7369
7370 union
7371 {
7372#ifdef MA_SUPPORT_WASAPI
7373 struct
7374 {
7375 ma_thread commandThread;
7376 ma_mutex commandLock;
7377 ma_semaphore commandSem;
7378 ma_uint32 commandIndex;
7379 ma_uint32 commandCount;
7380 ma_context_command__wasapi commands[4];
7381 ma_handle hAvrt;
7382 ma_proc AvSetMmThreadCharacteristicsA;
7383 ma_proc AvRevertMmThreadcharacteristics;
7384 ma_handle hMMDevapi;
7385 ma_proc ActivateAudioInterfaceAsync;
7386 } wasapi;
7387#endif
7388#ifdef MA_SUPPORT_DSOUND
7389 struct
7390 {
7391 ma_handle hWnd; /* Can be null. */
7392 ma_handle hDSoundDLL;
7393 ma_proc DirectSoundCreate;
7394 ma_proc DirectSoundEnumerateA;
7395 ma_proc DirectSoundCaptureCreate;
7396 ma_proc DirectSoundCaptureEnumerateA;
7397 } dsound;
7398#endif
7399#ifdef MA_SUPPORT_WINMM
7400 struct
7401 {
7402 ma_handle hWinMM;
7403 ma_proc waveOutGetNumDevs;
7404 ma_proc waveOutGetDevCapsA;
7405 ma_proc waveOutOpen;
7406 ma_proc waveOutClose;
7407 ma_proc waveOutPrepareHeader;
7408 ma_proc waveOutUnprepareHeader;
7409 ma_proc waveOutWrite;
7410 ma_proc waveOutReset;
7411 ma_proc waveInGetNumDevs;
7412 ma_proc waveInGetDevCapsA;
7413 ma_proc waveInOpen;
7414 ma_proc waveInClose;
7415 ma_proc waveInPrepareHeader;
7416 ma_proc waveInUnprepareHeader;
7417 ma_proc waveInAddBuffer;
7418 ma_proc waveInStart;
7419 ma_proc waveInReset;
7420 } winmm;
7421#endif
7422#ifdef MA_SUPPORT_ALSA
7423 struct
7424 {
7425 ma_handle asoundSO;
7426 ma_proc snd_pcm_open;
7427 ma_proc snd_pcm_close;
7428 ma_proc snd_pcm_hw_params_sizeof;
7429 ma_proc snd_pcm_hw_params_any;
7430 ma_proc snd_pcm_hw_params_set_format;
7431 ma_proc snd_pcm_hw_params_set_format_first;
7432 ma_proc snd_pcm_hw_params_get_format_mask;
7433 ma_proc snd_pcm_hw_params_set_channels;
7434 ma_proc snd_pcm_hw_params_set_channels_near;
7435 ma_proc snd_pcm_hw_params_set_channels_minmax;
7436 ma_proc snd_pcm_hw_params_set_rate_resample;
7437 ma_proc snd_pcm_hw_params_set_rate;
7438 ma_proc snd_pcm_hw_params_set_rate_near;
7439 ma_proc snd_pcm_hw_params_set_rate_minmax;
7440 ma_proc snd_pcm_hw_params_set_buffer_size_near;
7441 ma_proc snd_pcm_hw_params_set_periods_near;
7442 ma_proc snd_pcm_hw_params_set_access;
7443 ma_proc snd_pcm_hw_params_get_format;
7444 ma_proc snd_pcm_hw_params_get_channels;
7445 ma_proc snd_pcm_hw_params_get_channels_min;
7446 ma_proc snd_pcm_hw_params_get_channels_max;
7447 ma_proc snd_pcm_hw_params_get_rate;
7448 ma_proc snd_pcm_hw_params_get_rate_min;
7449 ma_proc snd_pcm_hw_params_get_rate_max;
7450 ma_proc snd_pcm_hw_params_get_buffer_size;
7451 ma_proc snd_pcm_hw_params_get_periods;
7452 ma_proc snd_pcm_hw_params_get_access;
7453 ma_proc snd_pcm_hw_params_test_format;
7454 ma_proc snd_pcm_hw_params_test_channels;
7455 ma_proc snd_pcm_hw_params_test_rate;
7456 ma_proc snd_pcm_hw_params;
7457 ma_proc snd_pcm_sw_params_sizeof;
7458 ma_proc snd_pcm_sw_params_current;
7459 ma_proc snd_pcm_sw_params_get_boundary;
7460 ma_proc snd_pcm_sw_params_set_avail_min;
7461 ma_proc snd_pcm_sw_params_set_start_threshold;
7462 ma_proc snd_pcm_sw_params_set_stop_threshold;
7463 ma_proc snd_pcm_sw_params;
7464 ma_proc snd_pcm_format_mask_sizeof;
7465 ma_proc snd_pcm_format_mask_test;
7466 ma_proc snd_pcm_get_chmap;
7467 ma_proc snd_pcm_state;
7468 ma_proc snd_pcm_prepare;
7469 ma_proc snd_pcm_start;
7470 ma_proc snd_pcm_drop;
7471 ma_proc snd_pcm_drain;
7472 ma_proc snd_pcm_reset;
7473 ma_proc snd_device_name_hint;
7474 ma_proc snd_device_name_get_hint;
7475 ma_proc snd_card_get_index;
7476 ma_proc snd_device_name_free_hint;
7477 ma_proc snd_pcm_mmap_begin;
7478 ma_proc snd_pcm_mmap_commit;
7479 ma_proc snd_pcm_recover;
7480 ma_proc snd_pcm_readi;
7481 ma_proc snd_pcm_writei;
7482 ma_proc snd_pcm_avail;
7483 ma_proc snd_pcm_avail_update;
7484 ma_proc snd_pcm_wait;
7485 ma_proc snd_pcm_nonblock;
7486 ma_proc snd_pcm_info;
7487 ma_proc snd_pcm_info_sizeof;
7488 ma_proc snd_pcm_info_get_name;
7489 ma_proc snd_pcm_poll_descriptors;
7490 ma_proc snd_pcm_poll_descriptors_count;
7491 ma_proc snd_pcm_poll_descriptors_revents;
7492 ma_proc snd_config_update_free_global;
7493
7494 ma_mutex internalDeviceEnumLock;
7495 ma_bool32 useVerboseDeviceEnumeration;
7496 } alsa;
7497#endif
7498#ifdef MA_SUPPORT_PULSEAUDIO
7499 struct
7500 {
7501 ma_handle pulseSO;
7502 ma_proc pa_mainloop_new;
7503 ma_proc pa_mainloop_free;
7504 ma_proc pa_mainloop_quit;
7505 ma_proc pa_mainloop_get_api;
7506 ma_proc pa_mainloop_iterate;
7507 ma_proc pa_mainloop_wakeup;
7508 ma_proc pa_threaded_mainloop_new;
7509 ma_proc pa_threaded_mainloop_free;
7510 ma_proc pa_threaded_mainloop_start;
7511 ma_proc pa_threaded_mainloop_stop;
7512 ma_proc pa_threaded_mainloop_lock;
7513 ma_proc pa_threaded_mainloop_unlock;
7514 ma_proc pa_threaded_mainloop_wait;
7515 ma_proc pa_threaded_mainloop_signal;
7516 ma_proc pa_threaded_mainloop_accept;
7517 ma_proc pa_threaded_mainloop_get_retval;
7518 ma_proc pa_threaded_mainloop_get_api;
7519 ma_proc pa_threaded_mainloop_in_thread;
7520 ma_proc pa_threaded_mainloop_set_name;
7521 ma_proc pa_context_new;
7522 ma_proc pa_context_unref;
7523 ma_proc pa_context_connect;
7524 ma_proc pa_context_disconnect;
7525 ma_proc pa_context_set_state_callback;
7526 ma_proc pa_context_get_state;
7527 ma_proc pa_context_get_sink_info_list;
7528 ma_proc pa_context_get_source_info_list;
7529 ma_proc pa_context_get_sink_info_by_name;
7530 ma_proc pa_context_get_source_info_by_name;
7531 ma_proc pa_operation_unref;
7532 ma_proc pa_operation_get_state;
7533 ma_proc pa_channel_map_init_extend;
7534 ma_proc pa_channel_map_valid;
7535 ma_proc pa_channel_map_compatible;
7536 ma_proc pa_stream_new;
7537 ma_proc pa_stream_unref;
7538 ma_proc pa_stream_connect_playback;
7539 ma_proc pa_stream_connect_record;
7540 ma_proc pa_stream_disconnect;
7541 ma_proc pa_stream_get_state;
7542 ma_proc pa_stream_get_sample_spec;
7543 ma_proc pa_stream_get_channel_map;
7544 ma_proc pa_stream_get_buffer_attr;
7545 ma_proc pa_stream_set_buffer_attr;
7546 ma_proc pa_stream_get_device_name;
7547 ma_proc pa_stream_set_write_callback;
7548 ma_proc pa_stream_set_read_callback;
7549 ma_proc pa_stream_set_suspended_callback;
7550 ma_proc pa_stream_set_moved_callback;
7551 ma_proc pa_stream_is_suspended;
7552 ma_proc pa_stream_flush;
7553 ma_proc pa_stream_drain;
7554 ma_proc pa_stream_is_corked;
7555 ma_proc pa_stream_cork;
7556 ma_proc pa_stream_trigger;
7557 ma_proc pa_stream_begin_write;
7558 ma_proc pa_stream_write;
7559 ma_proc pa_stream_peek;
7560 ma_proc pa_stream_drop;
7561 ma_proc pa_stream_writable_size;
7562 ma_proc pa_stream_readable_size;
7563
7564 /*pa_mainloop**/ ma_ptr pMainLoop;
7565 /*pa_context**/ ma_ptr pPulseContext;
7566 char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */
7567 char* pServerName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */
7568 } pulse;
7569#endif
7570#ifdef MA_SUPPORT_JACK
7571 struct
7572 {
7573 ma_handle jackSO;
7574 ma_proc jack_client_open;
7575 ma_proc jack_client_close;
7576 ma_proc jack_client_name_size;
7577 ma_proc jack_set_process_callback;
7578 ma_proc jack_set_buffer_size_callback;
7579 ma_proc jack_on_shutdown;
7580 ma_proc jack_get_sample_rate;
7581 ma_proc jack_get_buffer_size;
7582 ma_proc jack_get_ports;
7583 ma_proc jack_activate;
7584 ma_proc jack_deactivate;
7585 ma_proc jack_connect;
7586 ma_proc jack_port_register;
7587 ma_proc jack_port_name;
7588 ma_proc jack_port_get_buffer;
7589 ma_proc jack_free;
7590
7591 char* pClientName;
7592 ma_bool32 tryStartServer;
7593 } jack;
7594#endif
7595#ifdef MA_SUPPORT_COREAUDIO
7596 struct
7597 {
7598 ma_handle hCoreFoundation;
7599 ma_proc CFStringGetCString;
7600 ma_proc CFRelease;
7601
7602 ma_handle hCoreAudio;
7603 ma_proc AudioObjectGetPropertyData;
7604 ma_proc AudioObjectGetPropertyDataSize;
7605 ma_proc AudioObjectSetPropertyData;
7606 ma_proc AudioObjectAddPropertyListener;
7607 ma_proc AudioObjectRemovePropertyListener;
7608
7609 ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */
7610 ma_proc AudioComponentFindNext;
7611 ma_proc AudioComponentInstanceDispose;
7612 ma_proc AudioComponentInstanceNew;
7613 ma_proc AudioOutputUnitStart;
7614 ma_proc AudioOutputUnitStop;
7615 ma_proc AudioUnitAddPropertyListener;
7616 ma_proc AudioUnitGetPropertyInfo;
7617 ma_proc AudioUnitGetProperty;
7618 ma_proc AudioUnitSetProperty;
7619 ma_proc AudioUnitInitialize;
7620 ma_proc AudioUnitRender;
7621
7622 /*AudioComponent*/ ma_ptr component;
7623 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(). */
7624 } coreaudio;
7625#endif
7626#ifdef MA_SUPPORT_SNDIO
7627 struct
7628 {
7629 ma_handle sndioSO;
7630 ma_proc sio_open;
7631 ma_proc sio_close;
7632 ma_proc sio_setpar;
7633 ma_proc sio_getpar;
7634 ma_proc sio_getcap;
7635 ma_proc sio_start;
7636 ma_proc sio_stop;
7637 ma_proc sio_read;
7638 ma_proc sio_write;
7639 ma_proc sio_onmove;
7640 ma_proc sio_nfds;
7641 ma_proc sio_pollfd;
7642 ma_proc sio_revents;
7643 ma_proc sio_eof;
7644 ma_proc sio_setvol;
7645 ma_proc sio_onvol;
7646 ma_proc sio_initpar;
7647 } sndio;
7648#endif
7649#ifdef MA_SUPPORT_AUDIO4
7650 struct
7651 {
7652 int _unused;
7653 } audio4;
7654#endif
7655#ifdef MA_SUPPORT_OSS
7656 struct
7657 {
7658 int versionMajor;
7659 int versionMinor;
7660 } oss;
7661#endif
7662#ifdef MA_SUPPORT_AAUDIO
7663 struct
7664 {
7665 ma_handle hAAudio; /* libaaudio.so */
7666 ma_proc AAudio_createStreamBuilder;
7667 ma_proc AAudioStreamBuilder_delete;
7668 ma_proc AAudioStreamBuilder_setDeviceId;
7669 ma_proc AAudioStreamBuilder_setDirection;
7670 ma_proc AAudioStreamBuilder_setSharingMode;
7671 ma_proc AAudioStreamBuilder_setFormat;
7672 ma_proc AAudioStreamBuilder_setChannelCount;
7673 ma_proc AAudioStreamBuilder_setSampleRate;
7674 ma_proc AAudioStreamBuilder_setBufferCapacityInFrames;
7675 ma_proc AAudioStreamBuilder_setFramesPerDataCallback;
7676 ma_proc AAudioStreamBuilder_setDataCallback;
7677 ma_proc AAudioStreamBuilder_setErrorCallback;
7678 ma_proc AAudioStreamBuilder_setPerformanceMode;
7679 ma_proc AAudioStreamBuilder_setUsage;
7680 ma_proc AAudioStreamBuilder_setContentType;
7681 ma_proc AAudioStreamBuilder_setInputPreset;
7682 ma_proc AAudioStreamBuilder_setAllowedCapturePolicy;
7683 ma_proc AAudioStreamBuilder_openStream;
7684 ma_proc AAudioStream_close;
7685 ma_proc AAudioStream_getState;
7686 ma_proc AAudioStream_waitForStateChange;
7687 ma_proc AAudioStream_getFormat;
7688 ma_proc AAudioStream_getChannelCount;
7689 ma_proc AAudioStream_getSampleRate;
7690 ma_proc AAudioStream_getBufferCapacityInFrames;
7691 ma_proc AAudioStream_getFramesPerDataCallback;
7692 ma_proc AAudioStream_getFramesPerBurst;
7693 ma_proc AAudioStream_requestStart;
7694 ma_proc AAudioStream_requestStop;
7695 ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */
7696 } aaudio;
7697#endif
7698#ifdef MA_SUPPORT_OPENSL
7699 struct
7700 {
7701 ma_handle libOpenSLES;
7702 ma_handle SL_IID_ENGINE;
7703 ma_handle SL_IID_AUDIOIODEVICECAPABILITIES;
7704 ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
7705 ma_handle SL_IID_RECORD;
7706 ma_handle SL_IID_PLAY;
7707 ma_handle SL_IID_OUTPUTMIX;
7708 ma_handle SL_IID_ANDROIDCONFIGURATION;
7709 ma_proc slCreateEngine;
7710 } opensl;
7711#endif
7712#ifdef MA_SUPPORT_WEBAUDIO
7713 struct
7714 {
7715 int _unused;
7716 } webaudio;
7717#endif
7718#ifdef MA_SUPPORT_NULL
7719 struct
7720 {
7723#endif
7724 };
7725
7726 union
7727 {
7728#if defined(MA_WIN32)
7729 struct
7730 {
7731 /*HMODULE*/ ma_handle hOle32DLL;
7732 ma_proc CoInitialize;
7733 ma_proc CoInitializeEx;
7734 ma_proc CoUninitialize;
7735 ma_proc CoCreateInstance;
7736 ma_proc CoTaskMemFree;
7737 ma_proc PropVariantClear;
7738 ma_proc StringFromGUID2;
7739
7740 /*HMODULE*/ ma_handle hUser32DLL;
7741 ma_proc GetForegroundWindow;
7742 ma_proc GetDesktopWindow;
7743
7744 /*HMODULE*/ ma_handle hAdvapi32DLL;
7745 ma_proc RegOpenKeyExA;
7746 ma_proc RegCloseKey;
7747 ma_proc RegQueryValueExA;
7748
7749 /*HRESULT*/ long CoInitializeResult;
7750 } win32;
7751#endif
7752#ifdef MA_POSIX
7753 struct
7754 {
7755 int _unused;
7757#endif
7758 int _unused;
7759 };
7760};
7761
7763{
7767 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. */
7768 ma_device_data_proc onData; /* Set once at initialization time and should not be changed after. */
7769 ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */
7770 ma_stop_proc onStop; /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */
7771 void* pUserData; /* Application defined data. */
7777 ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */
7778 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(). */
7783 ma_atomic_float masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */
7784 ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */
7785 struct
7786 {
7790 struct
7791 {
7795 struct
7796 {
7797 ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */
7798 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. */
7799 char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */
7800 ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
7813 void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */
7815 ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */
7816 void* pInputCache; /* In external format. Can be null. */
7821 struct
7822 {
7823 ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */
7824 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. */
7825 char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */
7826 ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
7839 void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */
7841 ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */
7843
7844 union
7845 {
7846#ifdef MA_SUPPORT_WASAPI
7847 struct
7848 {
7849 /*IAudioClient**/ ma_ptr pAudioClientPlayback;
7850 /*IAudioClient**/ ma_ptr pAudioClientCapture;
7851 /*IAudioRenderClient**/ ma_ptr pRenderClient;
7852 /*IAudioCaptureClient**/ ma_ptr pCaptureClient;
7853 /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */
7854 ma_IMMNotificationClient notificationClient;
7855 /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */
7856 /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */
7857 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. */
7858 ma_uint32 actualBufferSizeInFramesCapture;
7859 ma_uint32 originalPeriodSizeInFrames;
7860 ma_uint32 originalPeriodSizeInMilliseconds;
7861 ma_uint32 originalPeriods;
7862 ma_performance_profile originalPerformanceProfile;
7863 ma_uint32 periodSizeInFramesPlayback;
7864 ma_uint32 periodSizeInFramesCapture;
7865 void* pMappedBufferCapture;
7866 ma_uint32 mappedBufferCaptureCap;
7867 ma_uint32 mappedBufferCaptureLen;
7868 void* pMappedBufferPlayback;
7869 ma_uint32 mappedBufferPlaybackCap;
7870 ma_uint32 mappedBufferPlaybackLen;
7871 ma_atomic_bool32 isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
7872 ma_atomic_bool32 isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */
7873 ma_uint32 loopbackProcessID;
7874 ma_bool8 loopbackProcessExclude;
7875 ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
7876 ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
7877 ma_bool8 noHardwareOffloading;
7878 ma_bool8 allowCaptureAutoStreamRouting;
7879 ma_bool8 allowPlaybackAutoStreamRouting;
7880 ma_bool8 isDetachedPlayback;
7881 ma_bool8 isDetachedCapture;
7882 ma_wasapi_usage usage;
7883 void* hAvrtHandle;
7884 ma_mutex rerouteLock;
7885 } wasapi;
7886#endif
7887#ifdef MA_SUPPORT_DSOUND
7888 struct
7889 {
7890 /*LPDIRECTSOUND*/ ma_ptr pPlayback;
7891 /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer;
7892 /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer;
7893 /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture;
7894 /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer;
7895 } dsound;
7896#endif
7897#ifdef MA_SUPPORT_WINMM
7898 struct
7899 {
7900 /*HWAVEOUT*/ ma_handle hDevicePlayback;
7901 /*HWAVEIN*/ ma_handle hDeviceCapture;
7902 /*HANDLE*/ ma_handle hEventPlayback;
7903 /*HANDLE*/ ma_handle hEventCapture;
7904 ma_uint32 fragmentSizeInFrames;
7905 ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */
7906 ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */
7907 ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */
7908 ma_uint32 headerFramesConsumedCapture; /* ^^^ */
7909 /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */
7910 /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */
7911 ma_uint8* pIntermediaryBufferPlayback;
7912 ma_uint8* pIntermediaryBufferCapture;
7913 ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */
7914 } winmm;
7915#endif
7916#ifdef MA_SUPPORT_ALSA
7917 struct
7918 {
7919 /*snd_pcm_t**/ ma_ptr pPCMPlayback;
7920 /*snd_pcm_t**/ ma_ptr pPCMCapture;
7921 /*struct pollfd**/ void* pPollDescriptorsPlayback;
7922 /*struct pollfd**/ void* pPollDescriptorsCapture;
7923 int pollDescriptorCountPlayback;
7924 int pollDescriptorCountCapture;
7925 int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */
7926 int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */
7927 ma_bool8 isUsingMMapPlayback;
7928 ma_bool8 isUsingMMapCapture;
7929 } alsa;
7930#endif
7931#ifdef MA_SUPPORT_PULSEAUDIO
7932 struct
7933 {
7934 /*pa_mainloop**/ ma_ptr pMainLoop;
7935 /*pa_context**/ ma_ptr pPulseContext;
7936 /*pa_stream**/ ma_ptr pStreamPlayback;
7937 /*pa_stream**/ ma_ptr pStreamCapture;
7938 } pulse;
7939#endif
7940#ifdef MA_SUPPORT_JACK
7941 struct
7942 {
7943 /*jack_client_t**/ ma_ptr pClient;
7944 /*jack_port_t**/ ma_ptr* ppPortsPlayback;
7945 /*jack_port_t**/ ma_ptr* ppPortsCapture;
7946 float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */
7947 float* pIntermediaryBufferCapture;
7948 } jack;
7949#endif
7950#ifdef MA_SUPPORT_COREAUDIO
7951 struct
7952 {
7953 ma_uint32 deviceObjectIDPlayback;
7954 ma_uint32 deviceObjectIDCapture;
7955 /*AudioUnit*/ ma_ptr audioUnitPlayback;
7956 /*AudioUnit*/ ma_ptr audioUnitCapture;
7957 /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */
7958 ma_uint32 audioBufferCapInFrames; /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */
7959 ma_event stopEvent;
7960 ma_uint32 originalPeriodSizeInFrames;
7961 ma_uint32 originalPeriodSizeInMilliseconds;
7962 ma_uint32 originalPeriods;
7963 ma_performance_profile originalPerformanceProfile;
7964 ma_bool32 isDefaultPlaybackDevice;
7965 ma_bool32 isDefaultCaptureDevice;
7966 ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
7967 ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
7968 void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */
7969 } coreaudio;
7970#endif
7971#ifdef MA_SUPPORT_SNDIO
7972 struct
7973 {
7974 ma_ptr handlePlayback;
7975 ma_ptr handleCapture;
7976 ma_bool32 isStartedPlayback;
7977 ma_bool32 isStartedCapture;
7978 } sndio;
7979#endif
7980#ifdef MA_SUPPORT_AUDIO4
7981 struct
7982 {
7983 int fdPlayback;
7984 int fdCapture;
7985 } audio4;
7986#endif
7987#ifdef MA_SUPPORT_OSS
7988 struct
7989 {
7990 int fdPlayback;
7991 int fdCapture;
7992 } oss;
7993#endif
7994#ifdef MA_SUPPORT_AAUDIO
7995 struct
7996 {
7997 /*AAudioStream**/ ma_ptr pStreamPlayback;
7998 /*AAudioStream**/ ma_ptr pStreamCapture;
7999 ma_mutex rerouteLock;
8000 ma_atomic_bool32 isTearingDown;
8001 ma_aaudio_usage usage;
8002 ma_aaudio_content_type contentType;
8003 ma_aaudio_input_preset inputPreset;
8004 ma_aaudio_allowed_capture_policy allowedCapturePolicy;
8005 ma_bool32 noAutoStartAfterReroute;
8006 } aaudio;
8007#endif
8008#ifdef MA_SUPPORT_OPENSL
8009 struct
8010 {
8011 /*SLObjectItf*/ ma_ptr pOutputMixObj;
8012 /*SLOutputMixItf*/ ma_ptr pOutputMix;
8013 /*SLObjectItf*/ ma_ptr pAudioPlayerObj;
8014 /*SLPlayItf*/ ma_ptr pAudioPlayer;
8015 /*SLObjectItf*/ ma_ptr pAudioRecorderObj;
8016 /*SLRecordItf*/ ma_ptr pAudioRecorder;
8017 /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback;
8018 /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture;
8019 ma_bool32 isDrainingCapture;
8020 ma_bool32 isDrainingPlayback;
8021 ma_uint32 currentBufferIndexPlayback;
8022 ma_uint32 currentBufferIndexCapture;
8023 ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */
8024 ma_uint8* pBufferCapture;
8025 } opensl;
8026#endif
8027#ifdef MA_SUPPORT_WEBAUDIO
8028 struct
8029 {
8030 /* AudioWorklets path. */
8031 /* EMSCRIPTEN_WEBAUDIO_T */ int audioContext;
8032 /* EMSCRIPTEN_WEBAUDIO_T */ int audioWorklet;
8033 float* pIntermediaryBuffer;
8034 void* pStackBuffer;
8035 ma_result initResult; /* Set to MA_BUSY while initialization is in progress. */
8036 int deviceIndex; /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */
8037 } webaudio;
8038#endif
8039#ifdef MA_SUPPORT_NULL
8040 struct
8041 {
8054 ma_atomic_bool32 isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */
8056#endif
8057 };
8058};
8059#if defined(_MSC_VER) && !defined(__clang__)
8060 #pragma warning(pop)
8061#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
8062 #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
8063#endif
8064
8065/*
8066Initializes a `ma_context_config` object.
8067
8068
8069Return Value
8070------------
8071A `ma_context_config` initialized to defaults.
8072
8073
8074Remarks
8075-------
8076You 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
8077is updated and new members are added to `ma_context_config`. It also sets logical defaults.
8078
8079You can override members of the returned object by changing it's members directly.
8080
8081
8082See Also
8083--------
8084ma_context_init()
8085*/
8087
8088/*
8089Initializes a context.
8090
8091The 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
8092device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices.
8093
8094
8095Parameters
8096----------
8097backends (in, optional)
8098 A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.
8099
8100backendCount (in, optional)
8101 The number of items in `backend`. Ignored if `backend` is NULL.
8102
8103pConfig (in, optional)
8104 The context configuration.
8105
8106pContext (in)
8107 A pointer to the context object being initialized.
8108
8109
8110Return Value
8111------------
8112MA_SUCCESS if successful; any other error code otherwise.
8113
8114
8115Thread Safety
8116-------------
8117Unsafe. Do not call this function across multiple threads as some backends read and write to global state.
8118
8119
8120Remarks
8121-------
8122When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order:
8123
8124 |-------------|-----------------------|--------------------------------------------------------|
8125 | Name | Enum Name | Supported Operating Systems |
8126 |-------------|-----------------------|--------------------------------------------------------|
8127 | WASAPI | ma_backend_wasapi | Windows Vista+ |
8128 | DirectSound | ma_backend_dsound | Windows XP+ |
8129 | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) |
8130 | Core Audio | ma_backend_coreaudio | macOS, iOS |
8131 | ALSA | ma_backend_alsa | Linux |
8132 | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) |
8133 | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) |
8134 | sndio | ma_backend_sndio | OpenBSD |
8135 | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD |
8136 | OSS | ma_backend_oss | FreeBSD |
8137 | AAudio | ma_backend_aaudio | Android 8+ |
8138 | OpenSL|ES | ma_backend_opensl | Android (API level 16+) |
8139 | Web Audio | ma_backend_webaudio | Web (via Emscripten) |
8140 | Null | ma_backend_null | Cross Platform (not used on Web) |
8141 |-------------|-----------------------|--------------------------------------------------------|
8142
8143The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings
8144can then be set directly on the structure. Below are the members of the `ma_context_config` object.
8145
8146 pLog
8147 A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not
8148 require logging. See the `ma_log` API for details on how to use the logging system.
8149
8150 threadPriority
8151 The desired priority to use for the audio thread. Allowable values include the following:
8152
8153 |--------------------------------------|
8154 | Thread Priority |
8155 |--------------------------------------|
8156 | ma_thread_priority_idle |
8157 | ma_thread_priority_lowest |
8158 | ma_thread_priority_low |
8159 | ma_thread_priority_normal |
8160 | ma_thread_priority_high |
8161 | ma_thread_priority_highest (default) |
8162 | ma_thread_priority_realtime |
8163 | ma_thread_priority_default |
8164 |--------------------------------------|
8165
8166 threadStackSize
8167 The desired size of the stack for the audio thread. Defaults to the operating system's default.
8168
8169 pUserData
8170 A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`.
8171
8172 allocationCallbacks
8173 Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation
8174 callbacks will be used for anything tied to the context, including devices.
8175
8176 alsa.useVerboseDeviceEnumeration
8177 ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique
8178 card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes
8179 it so the ALSA backend includes all devices. Defaults to false.
8180
8181 pulse.pApplicationName
8182 PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`.
8183
8184 pulse.pServerName
8185 PulseAudio only. The name of the server to connect to with `pa_context_connect()`.
8186
8187 pulse.tryAutoSpawn
8188 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
8189 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
8190 intrusive for the end user.
8191
8192 coreaudio.sessionCategory
8193 iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.
8194
8195 |-----------------------------------------|-------------------------------------|
8196 | miniaudio Token | Core Audio Token |
8197 |-----------------------------------------|-------------------------------------|
8198 | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient |
8199 | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient |
8200 | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback |
8201 | ma_ios_session_category_record | AVAudioSessionCategoryRecord |
8202 | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord |
8203 | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute |
8204 | ma_ios_session_category_none | AVAudioSessionCategoryAmbient |
8205 | ma_ios_session_category_default | AVAudioSessionCategoryAmbient |
8206 |-----------------------------------------|-------------------------------------|
8207
8208 coreaudio.sessionCategoryOptions
8209 iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents.
8210
8211 |---------------------------------------------------------------------------|------------------------------------------------------------------|
8212 | miniaudio Token | Core Audio Token |
8213 |---------------------------------------------------------------------------|------------------------------------------------------------------|
8214 | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers |
8215 | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers |
8216 | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth |
8217 | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker |
8218 | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers |
8219 | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP |
8220 | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay |
8221 |---------------------------------------------------------------------------|------------------------------------------------------------------|
8222
8223 coreaudio.noAudioSessionActivate
8224 iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization.
8225
8226 coreaudio.noAudioSessionDeactivate
8227 iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization.
8228
8229 jack.pClientName
8230 The name of the client to pass to `jack_client_open()`.
8231
8232 jack.tryStartServer
8233 Whether or not to try auto-starting the JACK server. Defaults to false.
8234
8235
8236It 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
8237relevant backends every time it's initialized.
8238
8239The location of the context cannot change throughout it's lifetime. Consider allocating the `ma_context` object with `malloc()` if this is an issue. The
8240reason for this is that a pointer to the context is stored in the `ma_device` structure.
8241
8242
8243Example 1 - Default Initialization
8244----------------------------------
8245The example below shows how to initialize the context using the default configuration.
8246
8247```c
8248ma_context context;
8249ma_result result = ma_context_init(NULL, 0, NULL, &context);
8250if (result != MA_SUCCESS) {
8251 // Error.
8252}
8253```
8254
8255
8256Example 2 - Custom Configuration
8257--------------------------------
8258The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program
8259wants 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
8260want an error to be returned if no valid backend is available which they achieve by excluding the Null backend.
8261
8262For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface.
8263
8264```c
8265ma_backend backends[] = {
8266 ma_backend_alsa,
8267 ma_backend_pulseaudio,
8268 ma_backend_wasapi,
8269 ma_backend_dsound
8270};
8271
8272ma_log log;
8273ma_log_init(&log);
8274ma_log_register_callback(&log, ma_log_callback_init(my_log_callbac, pMyLogUserData));
8275
8276ma_context_config config = ma_context_config_init();
8277config.pLog = &log; // Specify a custom log object in the config so any logs that are posted from ma_context_init() are captured.
8278
8279ma_context context;
8280ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context);
8281if (result != MA_SUCCESS) {
8282 // Error.
8283 if (result == MA_NO_BACKEND) {
8284 // Couldn't find an appropriate backend.
8285 }
8286}
8287
8288// You could also attach a log callback post-initialization:
8289ma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData));
8290```
8291
8292
8293See Also
8294--------
8295ma_context_config_init()
8296ma_context_uninit()
8297*/
8298MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext);
8299
8300/*
8301Uninitializes a context.
8302
8303
8304Return Value
8305------------
8306MA_SUCCESS if successful; any other error code otherwise.
8307
8308
8309Thread Safety
8310-------------
8311Unsafe. Do not call this function across multiple threads as some backends read and write to global state.
8312
8313
8314Remarks
8315-------
8316Results are undefined if you call this while any device created by this context is still active.
8317
8318
8319See Also
8320--------
8321ma_context_init()
8322*/
8324
8325/*
8326Retrieves the size of the ma_context object.
8327
8328This is mainly for the purpose of bindings to know how much memory to allocate.
8329*/
8331
8332/*
8333Retrieves a pointer to the log object associated with this context.
8334
8335
8336Remarks
8337-------
8338Pass the returned pointer to `ma_log_post()`, `ma_log_postv()` or `ma_log_postf()` to post a log
8339message.
8340
8341You can attach your own logging callback to the log with `ma_log_register_callback()`
8342
8343
8344Return Value
8345------------
8346A pointer to the `ma_log` object that the context uses to post log messages. If some error occurs,
8347NULL will be returned.
8348*/
8350
8351/*
8352Enumerates over every device (both playback and capture).
8353
8354This 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
8355an internal heap allocation, or it simply suits your code better.
8356
8357Note 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
8358opening 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,
8359but don't call it from within the enumeration callback.
8360
8361Returning false from the callback will stop enumeration. Returning true will continue enumeration.
8362
8363
8364Parameters
8365----------
8366pContext (in)
8367 A pointer to the context performing the enumeration.
8368
8369callback (in)
8370 The callback to fire for each enumerated device.
8371
8372pUserData (in)
8373 A pointer to application-defined data passed to the callback.
8374
8375
8376Return Value
8377------------
8378MA_SUCCESS if successful; any other error code otherwise.
8379
8380
8381Thread Safety
8382-------------
8383Safe. This is guarded using a simple mutex lock.
8384
8385
8386Remarks
8387-------
8388Do _not_ assume the first enumerated device of a given type is the default device.
8389
8390Some backends and platforms may only support default playback and capture devices.
8391
8392In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also,
8393do not try to call `ma_context_get_device_info()` from within the callback.
8394
8395Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation.
8396
8397
8398Example 1 - Simple Enumeration
8399------------------------------
8400ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
8401{
8402 printf("Device Name: %s\n", pInfo->name);
8403 return MA_TRUE;
8404}
8405
8406ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData);
8407if (result != MA_SUCCESS) {
8408 // Error.
8409}
8410
8411
8412See Also
8413--------
8414ma_context_get_devices()
8415*/
8417
8418/*
8419Retrieves basic information about every active playback and/or capture device.
8420
8421This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos`
8422parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback.
8423
8424Note 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
8425opening 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,
8426but don't call it from within the enumeration callback.
8427
8428
8429Parameters
8430----------
8431pContext (in)
8432 A pointer to the context performing the enumeration.
8433
8434ppPlaybackDeviceInfos (out)
8435 A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices.
8436
8437pPlaybackDeviceCount (out)
8438 A pointer to an unsigned integer that will receive the number of playback devices.
8439
8440ppCaptureDeviceInfos (out)
8441 A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices.
8442
8443pCaptureDeviceCount (out)
8444 A pointer to an unsigned integer that will receive the number of capture devices.
8445
8446
8447Return Value
8448------------
8449MA_SUCCESS if successful; any other error code otherwise.
8450
8451
8452Thread Safety
8453-------------
8454Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple
8455threads. Instead, you need to make a copy of the returned data with your own higher level synchronization.
8456
8457
8458Remarks
8459-------
8460It is _not_ safe to assume the first device in the list is the default device.
8461
8462You can pass in NULL for the playback or capture lists in which case they'll be ignored.
8463
8464The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers.
8465
8466
8467See Also
8468--------
8469ma_context_enumerate_devices()
8470*/
8471MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount);
8472
8473/*
8474Retrieves information about a device of the given type, with the specified ID and share mode.
8475
8476
8477Parameters
8478----------
8479pContext (in)
8480 A pointer to the context performing the query.
8481
8482deviceType (in)
8483 The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`.
8484
8485pDeviceID (in)
8486 The ID of the device being queried.
8487
8488pDeviceInfo (out)
8489 A pointer to the `ma_device_info` structure that will receive the device information.
8490
8491
8492Return Value
8493------------
8494MA_SUCCESS if successful; any other error code otherwise.
8495
8496
8497Thread Safety
8498-------------
8499Safe. This is guarded using a simple mutex lock.
8500
8501
8502Remarks
8503-------
8504Do _not_ call this from within the `ma_context_enumerate_devices()` callback.
8505
8506It'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
8507shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify
8508which 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
8509the requested share mode is unsupported.
8510
8511This leaves pDeviceInfo unmodified in the result of an error.
8512*/
8514
8515/*
8516Determines if the given context supports loopback mode.
8517
8518
8519Parameters
8520----------
8521pContext (in)
8522 A pointer to the context getting queried.
8523
8524
8525Return Value
8526------------
8527MA_TRUE if the context supports loopback mode; MA_FALSE otherwise.
8528*/
8530
8531
8532
8533/*
8534Initializes a device config with default settings.
8535
8536
8537Parameters
8538----------
8539deviceType (in)
8540 The type of the device this config is being initialized for. This must set to one of the following:
8541
8542 |-------------------------|
8543 | Device Type |
8544 |-------------------------|
8545 | ma_device_type_playback |
8546 | ma_device_type_capture |
8547 | ma_device_type_duplex |
8548 | ma_device_type_loopback |
8549 |-------------------------|
8550
8551
8552Return Value
8553------------
8554A new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks.
8555
8556
8557Thread Safety
8558-------------
8559Safe.
8560
8561
8562Callback Safety
8563---------------
8564Safe, but don't try initializing a device in a callback.
8565
8566
8567Remarks
8568-------
8569The 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
8570typical configuration which sets the sample format, channel count, sample rate, data callback and user data. These are usually things you will want to change
8571before initializing the device.
8572
8573See `ma_device_init()` for details on specific configuration options.
8574
8575
8576Example 1 - Simple Configuration
8577--------------------------------
8578The 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
8579then the returned object is modified directly. This is important because it ensures that your program continues to work as new configuration options are added
8580to the `ma_device_config` structure.
8581
8582```c
8583ma_device_config config = ma_device_config_init(ma_device_type_playback);
8584config.playback.format = ma_format_f32;
8585config.playback.channels = 2;
8586config.sampleRate = 48000;
8587config.dataCallback = ma_data_callback;
8588config.pUserData = pMyUserData;
8589```
8590
8591
8592See Also
8593--------
8594ma_device_init()
8595ma_device_init_ex()
8596*/
8598
8599
8600/*
8601Initializes a device.
8602
8603A 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
8604from 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
8605playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the
8606device is done via a callback which is fired by miniaudio at periodic time intervals.
8607
8608The frequency at which data is delivered to and from a device depends on the size of its period. The size of the period can be defined in terms of PCM frames
8609or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and
8610increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but
8611miniaudio'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
8612media 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
8613backend is ultimately responsible for what it gives you. You cannot assume you will get exactly what you ask for.
8614
8615When 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
8616format 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
8617can 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.
8618
8619
8620Parameters
8621----------
8622pContext (in, optional)
8623 A pointer to the context that owns the device. This can be null, in which case it creates a default context internally.
8624
8625pConfig (in)
8626 A pointer to the device configuration. Cannot be null. See remarks for details.
8627
8628pDevice (out)
8629 A pointer to the device object being initialized.
8630
8631
8632Return Value
8633------------
8634MA_SUCCESS if successful; any other error code otherwise.
8635
8636
8637Thread Safety
8638-------------
8639Unsafe. 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
8640calling this at the same time as `ma_device_uninit()`.
8641
8642
8643Callback Safety
8644---------------
8645Unsafe. It is not safe to call this inside any callback.
8646
8647
8648Remarks
8649-------
8650Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so:
8651
8652 ```c
8653 ma_context_init(NULL, 0, NULL, &context);
8654 ```
8655
8656Do 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
8657device.pContext for the initialization of other devices.
8658
8659The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can
8660then be set directly on the structure. Below are the members of the `ma_device_config` object.
8661
8662 deviceType
8663 Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`.
8664
8665 sampleRate
8666 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.
8667
8668 periodSizeInFrames
8669 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
8670 be used depending on the selected performance profile. This value affects latency. See below for details.
8671
8672 periodSizeInMilliseconds
8673 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
8674 used depending on the selected performance profile. The value affects latency. See below for details.
8675
8676 periods
8677 The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by
8678 this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured.
8679
8680 performanceProfile
8681 A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or
8682 `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at its default value.
8683
8684 noPreSilencedOutputBuffer
8685 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
8686 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
8687 callback will write to every sample in the output buffer, or if you are doing your own clearing.
8688
8689 noClip
8690 When set to true, the contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or
8691 not to clip. When set to false (default), the contents of the output buffer passed into the data callback will be clipped after returning. This only
8692 applies when the playback sample format is f32.
8693
8694 noDisableDenormals
8695 By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals.
8696
8697 noFixedSizedCallback
8698 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
8699 consistent frame count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to true, miniaudio will fire the callback with
8700 whatever the backend requests, which could be anything.
8701
8702 dataCallback
8703 The callback to fire whenever data is ready to be delivered to or from the device.
8704
8705 notificationCallback
8706 The callback to fire when something has changed with the device, such as whether or not it has been started or stopped.
8707
8708 pUserData
8709 The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`.
8710
8711 resampling.algorithm
8712 The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The
8713 default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`.
8714
8715 resampling.pBackendVTable
8716 A pointer to an optional vtable that can be used for plugging in a custom resampler.
8717
8718 resampling.pBackendUserData
8719 A pointer that will passed to callbacks in pBackendVTable.
8720
8721 resampling.linear.lpfOrder
8722 The linear resampler applies a low-pass filter as part of its processing for anti-aliasing. This setting controls the order of the filter. The higher
8723 the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is
8724 `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`.
8725
8726 playback.pDeviceID
8727 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
8728 default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration.
8729
8730 playback.format
8731 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
8732 initialization from the device object directly with `device.playback.format`.
8733
8734 playback.channels
8735 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
8736 from the device object directly with `device.playback.channels`.
8737
8738 playback.pChannelMap
8739 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
8740 device object direct with `device.playback.pChannelMap`. When set, the buffer should contain `channels` items.
8741
8742 playback.shareMode
8743 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
8744 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
8745 ma_share_mode_shared and reinitializing.
8746
8747 capture.pDeviceID
8748 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
8749 default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration.
8750
8751 capture.format
8752 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
8753 initialization from the device object directly with `device.capture.format`.
8754
8755 capture.channels
8756 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
8757 from the device object directly with `device.capture.channels`.
8758
8759 capture.pChannelMap
8760 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
8761 device object direct with `device.capture.pChannelMap`. When set, the buffer should contain `channels` items.
8762
8763 capture.shareMode
8764 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
8765 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
8766 ma_share_mode_shared and reinitializing.
8767
8768 wasapi.noAutoConvertSRC
8769 WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false.
8770
8771 wasapi.noDefaultQualitySRC
8772 WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`.
8773 You should usually leave this set to false, which is the default.
8774
8775 wasapi.noAutoStreamRouting
8776 WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false.
8777
8778 wasapi.noHardwareOffloading
8779 WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false.
8780
8781 alsa.noMMap
8782 ALSA only. When set to true, disables MMap mode. Defaults to false.
8783
8784 alsa.noAutoFormat
8785 ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false.
8786
8787 alsa.noAutoChannels
8788 ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false.
8789
8790 alsa.noAutoResample
8791 ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false.
8792
8793 pulse.pStreamNamePlayback
8794 PulseAudio only. Sets the stream name for playback.
8795
8796 pulse.pStreamNameCapture
8797 PulseAudio only. Sets the stream name for capture.
8798
8799 pulse.channelMap
8800 PulseAudio only. Sets the channel map that is requested from PulseAudio. See MA_PA_CHANNEL_MAP_* constants. Defaults to MA_PA_CHANNEL_MAP_AIFF.
8801
8802 coreaudio.allowNominalSampleRateChange
8803 Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This
8804 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
8805 that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will
8806 find the closest match between the sample rate requested in the device config and the sample rates natively supported by the
8807 hardware. When set to false, the sample rate currently set by the operating system will always be used.
8808
8809 opensl.streamType
8810 OpenSL only. Explicitly sets the stream type. If left unset (`ma_opensl_stream_type_default`), the
8811 stream type will be left unset. Think of this as the type of audio you're playing.
8812
8813 opensl.recordingPreset
8814 OpenSL only. Explicitly sets the type of recording your program will be doing. When left
8815 unset, the recording preset will be left unchanged.
8816
8817 aaudio.usage
8818 AAudio only. Explicitly sets the nature of the audio the program will be consuming. When
8819 left unset, the usage will be left unchanged.
8820
8821 aaudio.contentType
8822 AAudio only. Sets the content type. When left unset, the content type will be left unchanged.
8823
8824 aaudio.inputPreset
8825 AAudio only. Explicitly sets the type of recording your program will be doing. When left
8826 unset, the input preset will be left unchanged.
8827
8828 aaudio.noAutoStartAfterReroute
8829 AAudio only. Controls whether or not the device should be automatically restarted after a
8830 stream reroute. When set to false (default) the device will be restarted automatically;
8831 otherwise the device will be stopped.
8832
8833
8834Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device.
8835
8836After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`.
8837
8838If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or
8839`MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or
8840`ma_performance_profile_conservative`.
8841
8842If 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
8843in 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
8844config) 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,
8845for 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.
8846Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary.
8847
8848When sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by the config
8849and 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
8850on an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`,
8851`playback/capture.channels` and `sampleRate` members of the device object.
8852
8853When 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
8854asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information.
8855
8856ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture.
8857If these fail it will try falling back to the "hw" device.
8858
8859
8860Example 1 - Simple Initialization
8861---------------------------------
8862This 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
8863playback device this is usually all you need.
8864
8865```c
8866ma_device_config config = ma_device_config_init(ma_device_type_playback);
8867config.playback.format = ma_format_f32;
8868config.playback.channels = 2;
8869config.sampleRate = 48000;
8870config.dataCallback = ma_data_callback;
8871config.pMyUserData = pMyUserData;
8872
8873ma_device device;
8874ma_result result = ma_device_init(NULL, &config, &device);
8875if (result != MA_SUCCESS) {
8876 // Error
8877}
8878```
8879
8880
8881Example 2 - Advanced Initialization
8882-----------------------------------
8883This 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
8884and 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
8885enumeration.
8886
8887```c
8888ma_context context;
8889ma_result result = ma_context_init(NULL, 0, NULL, &context);
8890if (result != MA_SUCCESS) {
8891 // Error
8892}
8893
8894ma_device_info* pPlaybackDeviceInfos;
8895ma_uint32 playbackDeviceCount;
8896result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL);
8897if (result != MA_SUCCESS) {
8898 // Error
8899}
8900
8901// ... choose a device from pPlaybackDeviceInfos ...
8902
8903ma_device_config config = ma_device_config_init(ma_device_type_playback);
8904config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices().
8905config.playback.format = ma_format_f32;
8906config.playback.channels = 2;
8907config.sampleRate = 48000;
8908config.dataCallback = ma_data_callback;
8909config.pUserData = pMyUserData;
8910config.periodSizeInMilliseconds = 10;
8911config.periods = 3;
8912
8913ma_device device;
8914result = ma_device_init(&context, &config, &device);
8915if (result != MA_SUCCESS) {
8916 // Error
8917}
8918```
8919
8920
8921See Also
8922--------
8923ma_device_config_init()
8924ma_device_uninit()
8925ma_device_start()
8926ma_context_init()
8927ma_context_get_devices()
8928ma_context_enumerate_devices()
8929*/
8931
8932/*
8933Initializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context.
8934
8935This 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
8936allows you to configure the internally created context.
8937
8938
8939Parameters
8940----------
8941backends (in, optional)
8942 A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order.
8943
8944backendCount (in, optional)
8945 The number of items in `backend`. Ignored if `backend` is NULL.
8946
8947pContextConfig (in, optional)
8948 The context configuration.
8949
8950pConfig (in)
8951 A pointer to the device configuration. Cannot be null. See remarks for details.
8952
8953pDevice (out)
8954 A pointer to the device object being initialized.
8955
8956
8957Return Value
8958------------
8959MA_SUCCESS if successful; any other error code otherwise.
8960
8961
8962Thread Safety
8963-------------
8964Unsafe. 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
8965calling this at the same time as `ma_device_uninit()`.
8966
8967
8968Callback Safety
8969---------------
8970Unsafe. It is not safe to call this inside any callback.
8971
8972
8973Remarks
8974-------
8975You only need to use this function if you want to configure the context differently to its defaults. You should never use this function if you want to manage
8976your own context.
8977
8978See the documentation for `ma_context_init()` for information on the different context configuration options.
8979
8980
8981See Also
8982--------
8983ma_device_init()
8984ma_device_uninit()
8985ma_device_config_init()
8986ma_context_init()
8987*/
8988MA_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);
8989
8990/*
8991Uninitializes a device.
8992
8993This will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do.
8994
8995
8996Parameters
8997----------
8998pDevice (in)
8999 A pointer to the device to stop.
9000
9001
9002Return Value
9003------------
9004Nothing
9005
9006
9007Thread Safety
9008-------------
9009Unsafe. As soon as this API is called the device should be considered undefined.
9010
9011
9012Callback Safety
9013---------------
9014Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.
9015
9016
9017See Also
9018--------
9019ma_device_init()
9020ma_device_stop()
9021*/
9023
9024
9025/*
9026Retrieves a pointer to the context that owns the given device.
9027*/
9029
9030/*
9031Helper function for retrieving the log object associated with the context that owns this device.
9032*/
9034
9035
9036/*
9037Retrieves information about the device.
9038
9039
9040Parameters
9041----------
9042pDevice (in)
9043 A pointer to the device whose information is being retrieved.
9044
9045type (in)
9046 The device type. This parameter is required for duplex devices. When retrieving device
9047 information, you are doing so for an individual playback or capture device.
9048
9049pDeviceInfo (out)
9050 A pointer to the `ma_device_info` that will receive the device information.
9051
9052
9053Return Value
9054------------
9055MA_SUCCESS if successful; any other error code otherwise.
9056
9057
9058Thread Safety
9059-------------
9060Unsafe. This should be considered unsafe because it may be calling into the backend which may or
9061may not be safe.
9062
9063
9064Callback Safety
9065---------------
9066Unsafe. You should avoid calling this in the data callback because it may call into the backend
9067which may or may not be safe.
9068*/
9070
9071
9072/*
9073Retrieves the name of the device.
9074
9075
9076Parameters
9077----------
9078pDevice (in)
9079 A pointer to the device whose information is being retrieved.
9080
9081type (in)
9082 The device type. This parameter is required for duplex devices. When retrieving device
9083 information, you are doing so for an individual playback or capture device.
9084
9085pName (out)
9086 A pointer to the buffer that will receive the name.
9087
9088nameCap (in)
9089 The capacity of the output buffer, including space for the null terminator.
9090
9091pLengthNotIncludingNullTerminator (out, optional)
9092 A pointer to the variable that will receive the length of the name, not including the null
9093 terminator.
9094
9095
9096Return Value
9097------------
9098MA_SUCCESS if successful; any other error code otherwise.
9099
9100
9101Thread Safety
9102-------------
9103Unsafe. This should be considered unsafe because it may be calling into the backend which may or
9104may not be safe.
9105
9106
9107Callback Safety
9108---------------
9109Unsafe. You should avoid calling this in the data callback because it may call into the backend
9110which may or may not be safe.
9111
9112
9113Remarks
9114-------
9115If the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to
9116`pName` if you want to first get the length of the name for the purpose of memory allocation of the
9117output buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for
9118most cases and will avoid the need for the inefficiency of calling this function twice.
9119
9120This is implemented in terms of `ma_device_get_info()`.
9121*/
9122MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator);
9123
9124
9125/*
9126Starts the device. For playback devices this begins playback. For capture devices it begins recording.
9127
9128Use `ma_device_stop()` to stop the device.
9129
9130
9131Parameters
9132----------
9133pDevice (in)
9134 A pointer to the device to start.
9135
9136
9137Return Value
9138------------
9139MA_SUCCESS if successful; any other error code otherwise.
9140
9141
9142Thread Safety
9143-------------
9144Safe. It's safe to call this from any thread with the exception of the callback thread.
9145
9146
9147Callback Safety
9148---------------
9149Unsafe. It is not safe to call this inside any callback.
9150
9151
9152Remarks
9153-------
9154For 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
9155audio data in the buffer, which needs to be done before the device begins playback.
9156
9157This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety.
9158
9159Do not call this in any callback.
9160
9161
9162See Also
9163--------
9164ma_device_stop()
9165*/
9167
9168/*
9169Stops the device. For playback devices this stops playback. For capture devices it stops recording.
9170
9171Use `ma_device_start()` to start the device again.
9172
9173
9174Parameters
9175----------
9176pDevice (in)
9177 A pointer to the device to stop.
9178
9179
9180Return Value
9181------------
9182MA_SUCCESS if successful; any other error code otherwise.
9183
9184
9185Thread Safety
9186-------------
9187Safe. It's safe to call this from any thread with the exception of the callback thread.
9188
9189
9190Callback Safety
9191---------------
9192Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock.
9193
9194
9195Remarks
9196-------
9197This 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
9198backends 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
9199that was specified at initialization time).
9200
9201Backends 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
9202the 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
9203speakers or received from the microphone which can in turn result in de-syncs.
9204
9205Do not call this in any callback.
9206
9207
9208See Also
9209--------
9210ma_device_start()
9211*/
9213
9214/*
9215Determines whether or not the device is started.
9216
9217
9218Parameters
9219----------
9220pDevice (in)
9221 A pointer to the device whose start state is being retrieved.
9222
9223
9224Return Value
9225------------
9226True if the device is started, false otherwise.
9227
9228
9229Thread Safety
9230-------------
9231Safe. 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
9232value will be out of sync.
9233
9234
9235Callback Safety
9236---------------
9237Safe. This is implemented as a simple accessor.
9238
9239
9240See Also
9241--------
9242ma_device_start()
9243ma_device_stop()
9244*/
9246
9247
9248/*
9249Retrieves the state of the device.
9250
9251
9252Parameters
9253----------
9254pDevice (in)
9255 A pointer to the device whose state is being retrieved.
9256
9257
9258Return Value
9259------------
9260The current state of the device. The return value will be one of the following:
9261
9262 +-------------------------------+------------------------------------------------------------------------------+
9263 | ma_device_state_uninitialized | Will only be returned if the device is in the middle of initialization. |
9264 +-------------------------------+------------------------------------------------------------------------------+
9265 | ma_device_state_stopped | The device is stopped. The initial state of the device after initialization. |
9266 +-------------------------------+------------------------------------------------------------------------------+
9267 | ma_device_state_started | The device started and requesting and/or delivering audio data. |
9268 +-------------------------------+------------------------------------------------------------------------------+
9269 | ma_device_state_starting | The device is in the process of starting. |
9270 +-------------------------------+------------------------------------------------------------------------------+
9271 | ma_device_state_stopping | The device is in the process of stopping. |
9272 +-------------------------------+------------------------------------------------------------------------------+
9273
9274
9275Thread Safety
9276-------------
9277Safe. 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,
9278there's a possibility the return value could be out of sync. See remarks.
9279
9280
9281Callback Safety
9282---------------
9283Safe. This is implemented as a simple accessor.
9284
9285
9286Remarks
9287-------
9288The general flow of a devices state goes like this:
9289
9290 ```
9291 ma_device_init() -> ma_device_state_uninitialized -> ma_device_state_stopped
9292 ma_device_start() -> ma_device_state_starting -> ma_device_state_started
9293 ma_device_stop() -> ma_device_state_stopping -> ma_device_state_stopped
9294 ```
9295
9296When 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
9297value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own
9298synchronization.
9299*/
9301
9302
9303/*
9304Performs post backend initialization routines for setting up internal data conversion.
9305
9306This should be called whenever the backend is initialized. The only time this should be called from
9307outside of miniaudio is if you're implementing a custom backend, and you would only do it if you
9308are reinitializing the backend due to rerouting or reinitializing for some reason.
9309
9310
9311Parameters
9312----------
9313pDevice [in]
9314 A pointer to the device.
9315
9316deviceType [in]
9317 The type of the device that was just reinitialized.
9318
9319pPlaybackDescriptor [in]
9320 The descriptor of the playback device containing the internal data format and buffer sizes.
9321
9322pPlaybackDescriptor [in]
9323 The descriptor of the capture device containing the internal data format and buffer sizes.
9324
9325
9326Return Value
9327------------
9328MA_SUCCESS if successful; any other error otherwise.
9329
9330
9331Thread Safety
9332-------------
9333Unsafe. This will be reinitializing internal data converters which may be in use by another thread.
9334
9335
9336Callback Safety
9337---------------
9338Unsafe. This will be reinitializing internal data converters which may be in use by the callback.
9339
9340
9341Remarks
9342-------
9343For a duplex device, you can call this for only one side of the system. This is why the deviceType
9344is specified as a parameter rather than deriving it from the device.
9345
9346You do not need to call this manually unless you are doing a custom backend, in which case you need
9347only do it if you're manually performing rerouting or reinitialization.
9348*/
9349MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPlaybackDescriptor, const ma_device_descriptor* pCaptureDescriptor);
9350
9351
9352/*
9353Sets the master volume factor for the device.
9354
9355The 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
9356values less than 0 decreases the volume.
9357
9358
9359Parameters
9360----------
9361pDevice (in)
9362 A pointer to the device whose volume is being set.
9363
9364volume (in)
9365 The new volume factor. Must be >= 0.
9366
9367
9368Return Value
9369------------
9370MA_SUCCESS if the volume was set successfully.
9371MA_INVALID_ARGS if pDevice is NULL.
9372MA_INVALID_ARGS if volume is negative.
9373
9374
9375Thread Safety
9376-------------
9377Safe. This just sets a local member of the device object.
9378
9379
9380Callback Safety
9381---------------
9382Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.
9383
9384
9385Remarks
9386-------
9387This applies the volume factor across all channels.
9388
9389This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.
9390
9391
9392See Also
9393--------
9394ma_device_get_master_volume()
9395ma_device_set_master_volume_db()
9396ma_device_get_master_volume_db()
9397*/
9399
9400/*
9401Retrieves the master volume factor for the device.
9402
9403
9404Parameters
9405----------
9406pDevice (in)
9407 A pointer to the device whose volume factor is being retrieved.
9408
9409pVolume (in)
9410 A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1].
9411
9412
9413Return Value
9414------------
9415MA_SUCCESS if successful.
9416MA_INVALID_ARGS if pDevice is NULL.
9417MA_INVALID_ARGS if pVolume is NULL.
9418
9419
9420Thread Safety
9421-------------
9422Safe. This just a simple member retrieval.
9423
9424
9425Callback Safety
9426---------------
9427Safe.
9428
9429
9430Remarks
9431-------
9432If an error occurs, `*pVolume` will be set to 0.
9433
9434
9435See Also
9436--------
9437ma_device_set_master_volume()
9438ma_device_set_master_volume_gain_db()
9439ma_device_get_master_volume_gain_db()
9440*/
9442
9443/*
9444Sets the master volume for the device as gain in decibels.
9445
9446A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume.
9447
9448
9449Parameters
9450----------
9451pDevice (in)
9452 A pointer to the device whose gain is being set.
9453
9454gainDB (in)
9455 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.
9456
9457
9458Return Value
9459------------
9460MA_SUCCESS if the volume was set successfully.
9461MA_INVALID_ARGS if pDevice is NULL.
9462MA_INVALID_ARGS if the gain is > 0.
9463
9464
9465Thread Safety
9466-------------
9467Safe. This just sets a local member of the device object.
9468
9469
9470Callback Safety
9471---------------
9472Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied.
9473
9474
9475Remarks
9476-------
9477This applies the gain across all channels.
9478
9479This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream.
9480
9481
9482See Also
9483--------
9484ma_device_get_master_volume_gain_db()
9485ma_device_set_master_volume()
9486ma_device_get_master_volume()
9487*/
9489
9490/*
9491Retrieves the master gain in decibels.
9492
9493
9494Parameters
9495----------
9496pDevice (in)
9497 A pointer to the device whose gain is being retrieved.
9498
9499pGainDB (in)
9500 A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0.
9501
9502
9503Return Value
9504------------
9505MA_SUCCESS if successful.
9506MA_INVALID_ARGS if pDevice is NULL.
9507MA_INVALID_ARGS if pGainDB is NULL.
9508
9509
9510Thread Safety
9511-------------
9512Safe. This just a simple member retrieval.
9513
9514
9515Callback Safety
9516---------------
9517Safe.
9518
9519
9520Remarks
9521-------
9522If an error occurs, `*pGainDB` will be set to 0.
9523
9524
9525See Also
9526--------
9527ma_device_set_master_volume_db()
9528ma_device_set_master_volume()
9529ma_device_get_master_volume()
9530*/
9532
9533
9534/*
9535Called from the data callback of asynchronous backends to allow miniaudio to process the data and fire the miniaudio data callback.
9536
9537
9538Parameters
9539----------
9540pDevice (in)
9541 A pointer to device whose processing the data callback.
9542
9543pOutput (out)
9544 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
9545 this can be NULL, in which case pInput must not be NULL.
9546
9547pInput (in)
9548 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
9549 NULL, in which case `pOutput` must not be NULL.
9550
9551frameCount (in)
9552 The number of frames being processed.
9553
9554
9555Return Value
9556------------
9557MA_SUCCESS if successful; any other result code otherwise.
9558
9559
9560Thread Safety
9561-------------
9562This function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a
9563playback and capture device in duplex setups.
9564
9565
9566Callback Safety
9567---------------
9568Do not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend.
9569
9570
9571Remarks
9572-------
9573If both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in
9574which case `pInput` will be processed first, followed by `pOutput`.
9575
9576If you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that
9577callback.
9578*/
9579MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
9580
9581
9582/*
9583Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile.
9584
9585This function is used by backends for helping determine an appropriately sized buffer to use with
9586the device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the
9587`pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a
9588best guess at the device's native sample rate is also required which is where `nativeSampleRate`
9589comes in. In addition, the performance profile is also needed for cases where both the period size
9590in frames and milliseconds are both zero.
9591
9592
9593Parameters
9594----------
9595pDescriptor (in)
9596 A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members
9597 will be used for the calculation of the buffer size.
9598
9599nativeSampleRate (in)
9600 The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of
9601 `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which
9602 case a sample rate is required to convert to a size in frames.
9603
9604performanceProfile (in)
9605 When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are
9606 zero, miniaudio will fall back to a buffer size based on the performance profile. The profile
9607 to use for this calculation is determine by this parameter.
9608
9609
9610Return Value
9611------------
9612The calculated buffer size in frames.
9613
9614
9615Thread Safety
9616-------------
9617This is safe so long as nothing modifies `pDescriptor` at the same time. However, this function
9618should only ever be called from within the backend's device initialization routine and therefore
9619shouldn't have any multithreading concerns.
9620
9621
9622Callback Safety
9623---------------
9624This is safe to call within the data callback, but there is no reason to ever do this.
9625
9626
9627Remarks
9628-------
9629If `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that
9630is also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead.
9631*/
9633
9634
9635
9636/*
9637Retrieves a friendly name for a backend.
9638*/
9640
9641/*
9642Retrieves the backend enum from the given name.
9643*/
9644MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend);
9645
9646/*
9647Determines whether or not the given backend is available by the compilation environment.
9648*/
9650
9651/*
9652Retrieves compile-time enabled backends.
9653
9654
9655Parameters
9656----------
9657pBackends (out, optional)
9658 A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting
9659 the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends.
9660
9661backendCap (in)
9662 The capacity of the `pBackends` buffer.
9663
9664pBackendCount (out)
9665 A pointer to the variable that will receive the enabled backend count.
9666
9667
9668Return Value
9669------------
9670MA_SUCCESS if successful.
9671MA_INVALID_ARGS if `pBackendCount` is NULL.
9672MA_NO_SPACE if the capacity of `pBackends` is not large enough.
9673
9674If `MA_NO_SPACE` is returned, the `pBackends` buffer will be filled with `*pBackendCount` values.
9675
9676
9677Thread Safety
9678-------------
9679Safe.
9680
9681
9682Callback Safety
9683---------------
9684Safe.
9685
9686
9687Remarks
9688-------
9689If you want to retrieve the number of backends so you can determine the capacity of `pBackends` buffer, you can call
9690this function with `pBackends` set to NULL.
9691
9692This will also enumerate the null backend. If you don't want to include this you need to check for `ma_backend_null`
9693when you enumerate over the returned backends and handle it appropriately. Alternatively, you can disable it at
9694compile time with `MA_NO_NULL`.
9695
9696The returned backends are determined based on compile time settings, not the platform it's currently running on. For
9697example, PulseAudio will be returned if it was enabled at compile time, even when the user doesn't actually have
9698PulseAudio installed.
9699
9700
9701Example 1
9702---------
9703The example below retrieves the enabled backend count using a fixed sized buffer allocated on the stack. The buffer is
9704given a capacity of `MA_BACKEND_COUNT` which will guarantee it'll be large enough to store all available backends.
9705Since `MA_BACKEND_COUNT` is always a relatively small value, this should be suitable for most scenarios.
9706
9707```
9708ma_backend enabledBackends[MA_BACKEND_COUNT];
9709size_t enabledBackendCount;
9710
9711result = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount);
9712if (result != MA_SUCCESS) {
9713 // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid.
9714}
9715```
9716
9717
9718See Also
9719--------
9720ma_is_backend_enabled()
9721*/
9722MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount);
9723
9724/*
9725Determines whether or not loopback mode is support by a backend.
9726*/
9728
9729#endif /* MA_NO_DEVICE_IO */
9730
9731
9732
9733/************************************************************************************************************************************************************
9734
9735Utilities
9736
9737************************************************************************************************************************************************************/
9738
9739/*
9740Calculates a buffer size in milliseconds (rounded up) from the specified number of frames and sample rate.
9741*/
9743
9744/*
9745Calculates a buffer size in frames from the specified number of milliseconds and sample rate.
9746*/
9748
9749/*
9750Copies PCM frames from one buffer to another.
9751*/
9752MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
9753
9754/*
9755Copies silent frames into the given buffer.
9756
9757Remarks
9758-------
9759For 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
9760makes more sense for the purpose of mixing to initialize it to the center point.
9761*/
9762MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
9763
9764
9765/*
9766Offsets a pointer by the specified number of PCM frames.
9767*/
9768MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels);
9769MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels);
9770static 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); }
9771static 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); }
9772
9773
9774/*
9775Clips samples.
9776*/
9777MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count);
9778MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count);
9779MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count);
9780MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count);
9781MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count);
9782MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
9783
9784/*
9785Helper for applying a volume factor to samples.
9786
9787Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place.
9788*/
9789MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor);
9790MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor);
9791MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor);
9792MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor);
9793MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor);
9794
9795MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor);
9796MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor);
9797MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor);
9798MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor);
9799MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor);
9800
9801MA_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);
9802MA_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);
9803MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
9804MA_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);
9805MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
9806MA_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);
9807
9808MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
9809MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
9810MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
9811MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
9812MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
9813MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor);
9814
9815MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains);
9816
9817
9822MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume);
9823MA_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);
9824
9825
9826/*
9827Helper for converting a linear factor to gain in decibels.
9828*/
9829MA_API float ma_volume_linear_to_db(float factor);
9830
9831/*
9832Helper for converting gain in decibels to a linear factor.
9833*/
9835
9836
9837/*
9838Mixes the specified number of frames in floating point format with a volume factor.
9839
9840This will run on an optimized path when the volume is equal to 1.
9841*/
9842MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume);
9843
9844
9845
9846
9847/************************************************************************************************************************************************************
9848
9849VFS
9850===
9851
9852The 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
9853appropriate for a given situation.
9854
9855************************************************************************************************************************************************************/
9856typedef void ma_vfs;
9858
9859typedef enum
9860{
9861 MA_OPEN_MODE_READ = 0x00000001,
9864
9871
9872typedef struct
9873{
9875} ma_file_info;
9876
9877typedef struct
9878{
9879 ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
9880 ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
9882 ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead);
9883 ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten);
9884 ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin);
9885 ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor);
9888
9889MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
9890MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
9892MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead);
9893MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten);
9897MA_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);
9898
9899typedef struct
9900{
9902 ma_allocation_callbacks allocationCallbacks; /* Only used for the wchar_t version of open() on non-Windows platforms. */
9904
9906
9907
9908
9909typedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead);
9910typedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin);
9911typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor);
9912
9913
9914
9915#if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)
9924#endif
9925
9926/************************************************************************************************************************************************************
9927
9928Decoding
9929========
9930
9931Decoders 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
9932you do your own synchronization.
9933
9934************************************************************************************************************************************************************/
9935#ifndef MA_NO_DECODING
9936typedef struct ma_decoder ma_decoder;
9937
9938
9939typedef struct
9940{
9942 ma_uint32 seekPointCount; /* Set to > 0 to generate a seektable if the decoding backend supports it. */
9944
9946
9947
9948typedef struct
9949{
9950 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);
9951 ma_result (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */
9952 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. */
9953 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. */
9954 void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks);
9956
9957
9958typedef ma_result (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); /* Returns the number of bytes read. */
9959typedef ma_result (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin);
9960typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor);
9961
9962typedef struct
9963{
9964 ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */
9965 ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */
9966 ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */
9973 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. */
9978
9980{
9982 ma_data_source* pBackend; /* The decoding backend we'll be pulling data from. */
9983 const ma_decoding_backend_vtable* pBackendVTable; /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */
9989 ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */
9993 ma_data_converter converter; /* Data conversion is achieved by running frames through this. */
9994 void* pInputCache; /* In input format. Can be null if it's not needed. */
9995 ma_uint64 inputCacheCap; /* The capacity of the input cache. */
9996 ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */
9997 ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cache. */
9999 union
10000 {
10001 struct
10002 {
10006 struct
10007 {
10009 size_t dataSize;
10011 } memory; /* Only used for decoders that were opened against a block of memory. */
10013};
10014
10015MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate);
10017
10019MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
10020MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
10021MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
10022MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
10023MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
10024
10025/*
10026Uninitializes a decoder.
10027*/
10029
10030/*
10031Reads PCM frames from the given decoder.
10032
10033This is not thread safe without your own synchronization.
10034*/
10035MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
10036
10037/*
10038Seeks to a PCM frame based on its absolute index.
10039
10040This is not thread safe without your own synchronization.
10041*/
10043
10044/*
10045Retrieves the decoder's output data format.
10046*/
10047MA_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);
10048
10049/*
10050Retrieves the current position of the read cursor in PCM frames.
10051*/
10053
10054/*
10055Retrieves the length of the decoder in PCM frames.
10056
10057Do not call this on streams of an undefined length, such as internet radio.
10058
10059If the length is unknown or an error occurs, 0 will be returned.
10060
10061This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio
10062uses internally.
10063
10064For MP3's, this will decode the entire file. Do not call this in time critical scenarios.
10065
10066This function is not thread safe without your own synchronization.
10067*/
10069
10070/*
10071Retrieves the number of frames that can be read before reaching the end.
10072
10073This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in
10074particular ensuring you do not call it on streams of an undefined length, such as internet radio.
10075
10076If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be
10077returned.
10078*/
10080
10081/*
10082Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input,
10083pConfig should be set to what you want. On output it will be set to what you got.
10084*/
10085MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
10086MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
10087MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
10088
10089#endif /* MA_NO_DECODING */
10090
10091
10092/************************************************************************************************************************************************************
10093
10094Encoding
10095========
10096
10097Encoders do not perform any format conversion for you. If your target format does not support the format, and error will be returned.
10098
10099************************************************************************************************************************************************************/
10100#ifndef MA_NO_ENCODING
10101typedef struct ma_encoder ma_encoder;
10102
10103typedef ma_result (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten);
10104typedef ma_result (* ma_encoder_seek_proc) (ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin);
10106typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder);
10107typedef ma_result (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten);
10108
10117
10119
10139
10141MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
10142MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
10143MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
10144MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
10146MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten);
10147
10148#endif /* MA_NO_ENCODING */
10149
10150
10151/************************************************************************************************************************************************************
10152
10153Generation
10154
10155************************************************************************************************************************************************************/
10156#ifndef MA_NO_GENERATION
10164
10174
10175MA_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);
10176
10184
10187MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
10193
10203
10204MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency);
10205
10211
10214MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
10220
10227
10228
10238
10240
10241typedef struct
10242{
10246 union
10247 {
10248 struct
10249 {
10250 double** bin;
10253 } pink;
10254 struct
10255 {
10256 double* accumulation;
10257 } brownian;
10258 } state;
10259
10260 /* Memory management. */
10261 void* _pHeap;
10263} ma_noise;
10264
10265MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes);
10267MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise);
10268MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks);
10269MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
10273
10274#endif /* MA_NO_GENERATION */
10275
10276
10277
10278/************************************************************************************************************************************************************
10279
10280Resource Manager
10281
10282************************************************************************************************************************************************************/
10283/* The resource manager cannot be enabled if there is no decoder. */
10284#if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_NO_DECODING)
10285#define MA_NO_RESOURCE_MANAGER
10286#endif
10287
10288#ifndef MA_NO_RESOURCE_MANAGER
10294
10295typedef enum
10296{
10297 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. */
10298 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. */
10299 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */
10300 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(). */
10301 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. */
10302 MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING = 0x00000020 /* When set, configures the data source to loop by default. */
10304
10305
10306/*
10307Pipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional.
10308*/
10314
10315typedef struct
10316{
10317 ma_resource_manager_pipeline_stage_notification init; /* Initialization of the decoder. */
10320
10322
10323
10324
10325/* BEGIN BACKWARDS COMPATIBILITY */
10326/* TODO: Remove this block in version 0.12. */
10327#if 1
10328#define ma_resource_manager_job ma_job
10329#define ma_resource_manager_job_init ma_job_init
10330#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING MA_JOB_QUEUE_FLAG_NON_BLOCKING
10331#define ma_resource_manager_job_queue_config ma_job_queue_config
10332#define ma_resource_manager_job_queue_config_init ma_job_queue_config_init
10333#define ma_resource_manager_job_queue ma_job_queue
10334#define ma_resource_manager_job_queue_get_heap_size ma_job_queue_get_heap_size
10335#define ma_resource_manager_job_queue_init_preallocated ma_job_queue_init_preallocated
10336#define ma_resource_manager_job_queue_init ma_job_queue_init
10337#define ma_resource_manager_job_queue_uninit ma_job_queue_uninit
10338#define ma_resource_manager_job_queue_post ma_job_queue_post
10339#define ma_resource_manager_job_queue_next ma_job_queue_next
10340#endif
10341/* END BACKWARDS COMPATIBILITY */
10342
10343
10344
10345
10346/* 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. */
10347#ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT
10348#define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64
10349#endif
10350
10351typedef enum
10352{
10353 /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */
10355
10356 /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */
10359
10373
10375
10376
10377typedef enum
10378{
10379 ma_resource_manager_data_supply_type_unknown = 0, /* Used for determining whether or the data supply has been initialized. */
10380 ma_resource_manager_data_supply_type_encoded, /* Data supply is an encoded buffer. Connector is ma_decoder. */
10381 ma_resource_manager_data_supply_type_decoded, /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */
10382 ma_resource_manager_data_supply_type_decoded_paged /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */
10384
10385typedef struct
10386{
10387 MA_ATOMIC(4, ma_resource_manager_data_supply_type) type; /* Read and written from different threads so needs to be accessed atomically. */
10388 union
10389 {
10390 struct
10391 {
10392 const void* pData;
10394 } encoded;
10395 struct
10396 {
10397 const void* pData;
10403 } decoded;
10404 struct
10405 {
10409 } decodedPaged;
10412
10414{
10415 ma_uint32 hashedName32; /* The hashed name. This is the key. */
10417 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. */
10418 MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */
10419 MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
10420 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_*()). */
10425};
10426
10428{
10429 ma_data_source_base ds; /* Base data source. A data buffer is a data source. */
10430 ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this buffer. */
10431 ma_resource_manager_data_buffer_node* pNode; /* The data node. This is reference counted and is what supplies the data. */
10432 ma_uint32 flags; /* The flags that were passed used to initialize the buffer. */
10433 MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */
10434 MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
10435 ma_uint64 seekTargetInPCMFrames; /* Only updated by the public API. Never written nor read from the job thread. */
10436 ma_bool32 seekToCursorOnNextRead; /* On the next read we need to seek to the frame cursor. */
10437 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. */
10438 MA_ATOMIC(4, ma_bool32) isLooping; /* Can be read and written by different threads at the same time. Must be used atomically. */
10439 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. */
10440 union
10441 {
10442 ma_decoder decoder; /* Supply type is ma_resource_manager_data_supply_type_encoded */
10443 ma_audio_buffer buffer; /* Supply type is ma_resource_manager_data_supply_type_decoded */
10444 ma_paged_audio_buffer pagedBuffer; /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */
10445 } connector; /* Connects this object to the node's data supply. */
10446};
10447
10449{
10450 ma_data_source_base ds; /* Base data source. A data stream is a data source. */
10451 ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this data stream. */
10452 ma_uint32 flags; /* The flags that were passed used to initialize the stream. */
10453 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. */
10454 ma_bool32 isDecoderInitialized; /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */
10455 ma_uint64 totalLengthInPCMFrames; /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */
10456 ma_uint32 relativeCursor; /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */
10457 MA_ATOMIC(8, ma_uint64) absoluteCursor; /* The playback cursor, in absolute position starting from the start of the file. */
10458 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. */
10459 MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */
10460 MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
10461
10462 /* Written by the public API, read by the job thread. */
10463 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. */
10464
10465 /* Written by the job thread, read by the public API. */
10466 void* pPageData; /* Buffer containing the decoded data of each page. Allocated once at initialization time. */
10467 MA_ATOMIC(4, ma_uint32) pageFrameCount[2]; /* The number of valid PCM frames in each page. Used to determine the last valid frame. */
10468
10469 /* Written and read by both the public API and the job thread. These must be atomic. */
10470 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. */
10471 MA_ATOMIC(4, ma_bool32) isDecoderAtEnd; /* Whether or not the decoder has reached the end. */
10472 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. */
10473 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. */
10474};
10475
10477{
10478 union
10479 {
10482 } backend; /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */
10483
10484 ma_uint32 flags; /* The flags that were passed in to ma_resource_manager_data_source_init(). */
10485 MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */
10486 MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */
10487};
10488
10489typedef struct
10490{
10493 ma_format decodedFormat; /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */
10494 ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */
10495 ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */
10496 ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */
10498 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. */
10500 ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */
10505
10507
10509{
10511 ma_resource_manager_data_buffer_node* pRootDataBufferNode; /* The root buffer in the binary tree. */
10512#ifndef MA_NO_THREADING
10513 ma_mutex dataBufferBSTLock; /* For synchronizing access to the data buffer binary tree. */
10514 ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]; /* The threads for executing jobs. */
10515#endif
10516 ma_job_queue jobQueue; /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */
10517 ma_default_vfs defaultVFS; /* Only used if a custom VFS is not specified. */
10518 ma_log log; /* Only used if no log was specified in the config. */
10519};
10520
10521/* Init. */
10525
10526/* Registration. */
10527MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags);
10528MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags);
10529MA_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. */
10530MA_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);
10531MA_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. */
10532MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes);
10537
10538/* Data Buffers. */
10546MA_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);
10553
10554/* Data Streams. */
10561MA_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);
10568
10569/* Data Sources. */
10577MA_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);
10584
10585/* Job management. */
10587MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager); /* Helper for posting a quit job. */
10589MA_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. */
10590MA_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. */
10591#endif /* MA_NO_RESOURCE_MANAGER */
10592
10593
10594
10595/************************************************************************************************************************************************************
10596
10597Node Graph
10598
10599************************************************************************************************************************************************************/
10600#ifndef MA_NO_NODE_GRAPH
10601/* Must never exceed 254. */
10602#ifndef MA_MAX_NODE_BUS_COUNT
10603#define MA_MAX_NODE_BUS_COUNT 254
10604#endif
10605
10606/* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */
10607#ifndef MA_MAX_NODE_LOCAL_BUS_COUNT
10608#define MA_MAX_NODE_LOCAL_BUS_COUNT 2
10609#endif
10610
10611/* Use this when the bus count is determined by the node instance rather than the vtable. */
10612#define MA_NODE_BUS_COUNT_UNKNOWN 255
10613
10614
10615/* For some internal memory management of ma_node_graph. */
10616typedef struct
10617{
10618 size_t offset;
10620 unsigned char _data[1];
10621} ma_stack;
10622
10623
10625typedef void ma_node;
10626
10627
10628/* Node flags. */
10637
10638
10639/* The playback state of a node. Either started or stopped. */
10645
10646
10647typedef struct
10648{
10649 /*
10650 Extended processing callback. This callback is used for effects that process input and output
10651 at different rates (i.e. they perform resampling). This is similar to the simple version, only
10652 they take two separate frame counts: one for input, and one for output.
10653
10654 On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas
10655 `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`.
10656
10657 On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set
10658 `pFrameCountIn` to the number of input frames that were consumed.
10659 */
10660 void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut);
10661
10662 /*
10663 A callback for retrieving the number of input frames that are required to output the
10664 specified number of output frames. You would only want to implement this when the node performs
10665 resampling. This is optional, even for nodes that perform resampling, but it does offer a
10666 small reduction in latency as it allows miniaudio to calculate the exact number of input frames
10667 to read at a time instead of having to estimate.
10668 */
10669 ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount);
10670
10671 /*
10672 The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn`
10673 parameters of the callbacks above.
10674 */
10676
10677 /*
10678 The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut`
10679 parameters of the callbacks above.
10680 */
10682
10683 /*
10684 Flags describing characteristics of the node. This is currently just a placeholder for some
10685 ideas for later on.
10686 */
10689
10690typedef struct
10691{
10692 const ma_node_vtable* vtable; /* Should never be null. Initialization of the node will fail if so. */
10693 ma_node_state initialState; /* Defaults to ma_node_state_started. */
10694 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). */
10695 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). */
10696 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`. */
10697 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`. */
10699
10701
10702
10703/*
10704A node has multiple output buses. An output bus is attached to an input bus as an item in a linked
10705list. Think of the input bus as a linked list, with the output bus being an item in that list.
10706*/
10709{
10710 /* Immutable. */
10711 ma_node* pNode; /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */
10712 ma_uint8 outputBusIndex; /* The index of the output bus on pNode that this output bus represents. */
10713 ma_uint8 channels; /* The number of channels in the audio stream for this bus. */
10714
10715 /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */
10716 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. */
10717 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_*. */
10718 MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */
10719 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. */
10720 MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */
10721 MA_ATOMIC(4, float) volume; /* Linear. */
10722 MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext; /* If null, it's the tail node or detached. */
10723 MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev; /* If null, it's the head node or detached. */
10724 MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode; /* The node that this output bus is attached to. Required for detaching. */
10725};
10726
10727/*
10728A node has multiple input buses. The output buses of a node are connecting to the input busses of
10729another. An input bus is essentially just a linked list of output buses.
10730*/
10733{
10734 /* Mutable via multiple threads. */
10735 ma_node_output_bus head; /* Dummy head node for simplifying some lock-free thread-safety stuff. */
10736 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. */
10737 MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */
10738
10739 /* Set once at startup. */
10740 ma_uint8 channels; /* The number of channels in the audio stream for this bus. */
10741};
10742
10743
10746{
10747 /* These variables are set once at startup. */
10748 ma_node_graph* pNodeGraph; /* The graph this node belongs to. */
10754 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. */
10755 ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */
10756
10757 /* These variables are read and written only from the audio thread. */
10761
10762 /* These variables are read and written between different threads. */
10763 MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */
10764 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. */
10765 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()`. */
10766
10767 /* Memory management. */
10770 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. */
10771 ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */
10772};
10773
10774MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes);
10775MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode);
10776MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode);
10777MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10783MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex);
10787MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex);
10796
10797
10798typedef struct
10799{
10801 ma_uint32 processingSizeInFrames; /* This is the preferred processing size for node processing callbacks unless overridden by a node itself. Can be 0 in which case it will be based on the frame count passed into ma_node_graph_read_pcm_frames(), but will not be well defined. */
10802 size_t preMixStackSizeInBytes; /* Defaults to 512KB per channel. Reducing this will save memory, but the depth of your node graph will be more restricted. */
10804
10806
10807
10809{
10810 /* Immutable. */
10811 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. */
10812 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(). */
10813 float* pProcessingCache; /* This will be allocated when processingSizeInFrames is non-zero. This is needed because ma_node_graph_read_pcm_frames() can be called with a variable number of frames, and we may need to do some buffering in situations where the caller requests a frame count that's not a multiple of processingSizeInFrames. */
10816
10817 /* Read and written by multiple threads. */
10818 MA_ATOMIC(4, ma_bool32) isReading;
10819
10820 /* Modified only by the audio thread. */
10822};
10823
10824MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph);
10825MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks);
10827MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
10831
10832
10833
10834/* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */
10840
10842
10843
10849
10851MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks);
10854
10855
10856/* Splitter Node. 1 input, many outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */
10863
10865
10866
10867typedef struct
10868{
10871
10872MA_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);
10873MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks);
10874
10875
10876/*
10877Biquad Node
10878*/
10884
10885MA_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);
10886
10887
10893
10896MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10897
10898
10899/*
10900Low Pass Filter Node
10901*/
10907
10908MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
10909
10910
10911typedef struct
10912{
10915} ma_lpf_node;
10916
10917MA_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);
10919MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10920
10921
10922/*
10923High Pass Filter Node
10924*/
10930
10931MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
10932
10933
10934typedef struct
10935{
10938} ma_hpf_node;
10939
10940MA_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);
10942MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10943
10944
10945/*
10946Band Pass Filter Node
10947*/
10953
10954MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
10955
10956
10957typedef struct
10958{
10961} ma_bpf_node;
10962
10963MA_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);
10965MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10966
10967
10968/*
10969Notching Filter Node
10970*/
10976
10977MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency);
10978
10979
10985
10986MA_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);
10988MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
10989
10990
10991/*
10992Peaking Filter Node
10993*/
10999
11000MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
11001
11002
11003typedef struct
11004{
11007} ma_peak_node;
11008
11009MA_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);
11011MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
11012
11013
11014/*
11015Low Shelf Filter Node
11016*/
11022
11023MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
11024
11025
11031
11035
11036
11037/*
11038High Shelf Filter Node
11039*/
11045
11046MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
11047
11048
11054
11058
11059
11065
11066MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay);
11067
11068
11074
11075MA_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);
11076MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks);
11077MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value);
11079MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value);
11081MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value);
11083#endif /* MA_NO_NODE_GRAPH */
11084
11085
11086/* SECTION: miniaudio_engine.h */
11087/************************************************************************************************************************************************************
11088
11089Engine
11090
11091************************************************************************************************************************************************************/
11092#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH)
11093typedef struct ma_engine ma_engine;
11094typedef struct ma_sound ma_sound;
11095
11096
11097/* Sound flags. */
11098typedef enum
11099{
11100 /* Resource manager flags. */
11101 MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */
11102 MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */
11103 MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */
11104 MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */
11105 MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */
11106 MA_SOUND_FLAG_LOOPING = 0x00000020, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING */
11107
11108 /* ma_sound specific flags. */
11109 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. */
11110 MA_SOUND_FLAG_NO_PITCH = 0x00002000, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */
11111 MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00004000 /* Disable spatialization. */
11113
11114#ifndef MA_ENGINE_MAX_LISTENERS
11115#define MA_ENGINE_MAX_LISTENERS 4
11116#endif
11117
11118#define MA_LISTENER_INDEX_CLOSEST ((ma_uint8)-1)
11119
11125
11126typedef struct
11127{
11132 ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */
11133 ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */
11135 ma_bool8 isPitchDisabled; /* Pitching can be explicitly disabled with MA_SOUND_FLAG_NO_PITCH to optimize processing. */
11136 ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */
11137 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. */
11139
11141
11142
11143/* Base node object for both ma_sound and ma_sound_group. */
11144typedef struct
11145{
11146 ma_node_base baseNode; /* Must be the first member for compatibility with the ma_node API. */
11147 ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */
11148 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. */
11152 ma_linear_resampler resampler; /* For pitch shift. */
11155 ma_gainer volumeGainer; /* This will only be used if volumeSmoothTimeInPCMFrames is > 0. */
11156 ma_atomic_float volume; /* Defaults to 1. */
11157 MA_ATOMIC(4, float) pitch;
11158 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. */
11159 float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */
11160 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. */
11161 MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */
11162 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. */
11163
11164 /* When setting a fade, it's not done immediately in ma_sound_set_fade(). It's deferred to the audio thread which means we need to store the settings here. */
11165 struct
11166 {
11167 ma_atomic_float volumeBeg;
11168 ma_atomic_float volumeEnd;
11169 ma_atomic_uint64 fadeLengthInFrames; /* <-- Defaults to (~(ma_uint64)0) which is used to indicate that no fade should be applied. */
11170 ma_atomic_uint64 absoluteGlobalTimeInFrames; /* <-- The time to start the fade. */
11171 } fadeSettings;
11172
11173 /* Memory management. */
11175 void* _pHeap;
11177
11178MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes);
11180MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode);
11181MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks);
11182
11183
11184#define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF
11185
11186/* Callback for when a sound reaches the end. */
11187typedef void (* ma_sound_end_proc)(void* pUserData, ma_sound* pSound);
11188
11189typedef struct
11190{
11191 const char* pFilePath; /* Set this to load from the resource manager. */
11192 const wchar_t* pFilePathW; /* Set this to load from the resource manager. */
11193 ma_data_source* pDataSource; /* Set this to load from an existing data source. */
11194 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`. */
11195 ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */
11196 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. */
11197 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). */
11198 ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */
11199 ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */
11200 ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */
11201 ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */
11206 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_*(). */
11208#ifndef MA_NO_RESOURCE_MANAGER
11210#endif
11211 ma_fence* pDoneFence; /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */
11212 ma_bool32 isLooping; /* Deprecated. Use the MA_SOUND_FLAG_LOOPING flag in `flags` instead. */
11214
11215MA_API ma_sound_config ma_sound_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */
11216MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */
11217
11219{
11220 ma_engine_node engineNode; /* Must be the first member for compatibility with the ma_node API. */
11222 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. */
11227
11228 /*
11229 We're declaring a resource manager data source object here to save us a malloc when loading a
11230 sound via the resource manager, which I *think* will be the most common scenario.
11231 */
11232#ifndef MA_NO_RESOURCE_MANAGER
11234#endif
11235};
11236
11237/* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */
11245
11246/* A sound group is just a sound. */
11249
11250MA_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. */
11251MA_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. */
11252
11253typedef void (* ma_engine_process_proc)(void* pUserData, float* pFramesOut, ma_uint64 frameCount);
11254
11255typedef struct
11256{
11257#if !defined(MA_NO_RESOURCE_MANAGER)
11258 ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */
11259#endif
11260#if !defined(MA_NO_DEVICE_IO)
11262 ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */
11263 ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */
11264 ma_device_data_proc dataCallback; /* Can be null. Can be used to provide a custom device data callback. */
11266#endif
11267 ma_log* pLog; /* When set to NULL, will use the context's log. */
11268 ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */
11269 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. */
11270 ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */
11271 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.*/
11272 ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */
11273 ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */
11274 ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */
11275 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). */
11276 ma_uint32 preMixStackSizeInBytes; /* A stack is used for internal processing in the node graph. This allows you to configure the size of this stack. Smaller values will reduce the maximum depth of your node graph. You should rarely need to modify this. */
11278 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(). */
11279 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. */
11280 ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */
11281 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. */
11282 ma_engine_process_proc onProcess; /* Fired at the end of each call to ma_engine_read_pcm_frames(). For engine's that manage their own internal device (the default configuration), this will be fired from the audio thread, and you do not need to call ma_engine_read_pcm_frames() manually in order to trigger this. */
11283 void* pProcessUserData; /* User data that's passed into onProcess. */
11285
11287
11288
11290{
11291 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. */
11292#if !defined(MA_NO_RESOURCE_MANAGER)
11294#endif
11295#if !defined(MA_NO_DEVICE_IO)
11296 ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */
11297#endif
11305 ma_spinlock inlinedSoundLock; /* For synchronizing access to the inlined sound list. */
11306 ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */
11307 MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */
11308 ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */
11313};
11314
11317MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
11319#if !defined(MA_NO_RESOURCE_MANAGER)
11321#endif
11329MA_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. */
11330MA_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. */
11333
11340
11342MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ);
11343MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
11345MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
11347MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
11349MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
11350MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
11351MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
11353MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled);
11355
11356#ifndef MA_NO_RESOURCE_MANAGER
11357MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex);
11358MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */
11359#endif
11360
11361#ifndef MA_NO_RESOURCE_MANAGER
11362MA_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);
11363MA_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);
11364MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound);
11365#endif
11373MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */
11374MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */
11375MA_API void ma_sound_set_volume(ma_sound* pSound, float volume);
11377MA_API void ma_sound_set_pan(ma_sound* pSound, float pan);
11378MA_API float ma_sound_get_pan(const ma_sound* pSound);
11381MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch);
11389MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z);
11391MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z);
11393MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z);
11399MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff);
11401MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain);
11403MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain);
11405MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance);
11407MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance);
11409MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
11410MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
11411MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor);
11413MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor);
11415MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames);
11416MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds);
11417MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames);
11418MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds);
11420MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames);
11421MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds);
11422MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames);
11423MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds);
11424MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames);
11425MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds);
11432MA_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(). */
11433MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds); /* Abstraction to ma_sound_seek_to_pcm_frame() */
11434MA_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);
11440
11461MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z);
11463MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z);
11465MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z);
11481MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
11482MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
11485MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor);
11487MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames);
11488MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds);
11493MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds);
11496#endif /* MA_NO_ENGINE */
11497/* END SECTION: miniaudio_engine.h */
11498
11499#ifdef __cplusplus
11500}
11501#endif
11502#endif /* miniaudio_h */
11503
11504
11505/*
11506This is for preventing greying out of the implementation section.
11507*/
11508#if defined(Q_CREATOR_RUN) || defined(__INTELLISENSE__) || defined(__CDT_PARSER__)
11509#define MINIAUDIO_IMPLEMENTATION
11510#endif
11511
11512/************************************************************************************************************************************************************
11513*************************************************************************************************************************************************************
11514
11515IMPLEMENTATION
11516
11517*************************************************************************************************************************************************************
11518************************************************************************************************************************************************************/
11519#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
11520#ifndef miniaudio_c
11521#define miniaudio_c
11522
11523#include <assert.h>
11524#include <limits.h> /* For INT_MAX */
11525#include <math.h> /* sin(), etc. */
11526#include <stdlib.h> /* For malloc(), free(), wcstombs(). */
11527#include <string.h> /* For memset() */
11528
11529#include <stdarg.h>
11530#include <stdio.h>
11531#if !defined(_MSC_VER) && !defined(__DMC__)
11532 #include <strings.h> /* For strcasecmp(). */
11533 #include <wchar.h> /* For wcslen(), wcsrtombs() */
11534#endif
11535#ifdef _MSC_VER
11536 #include <float.h> /* For _controlfp_s constants */
11537#endif
11538
11539#if defined(MA_WIN32)
11540 #include <windows.h>
11541
11542 /*
11543 There's a possibility that WIN32_LEAN_AND_MEAN has been defined which will exclude some symbols
11544 such as STGM_READ and CLSCTL_ALL. We need to check these and define them ourselves if they're
11545 unavailable.
11546 */
11547 #ifndef STGM_READ
11548 #define STGM_READ 0x00000000L
11549 #endif
11550 #ifndef CLSCTX_ALL
11551 #define CLSCTX_ALL 23
11552 #endif
11553
11554 /* IUnknown is used by both the WASAPI and DirectSound backends. It easier to just declare our version here. */
11555 typedef struct ma_IUnknown ma_IUnknown;
11556#endif
11557
11558#if !defined(MA_WIN32)
11559 #if !defined(MA_NO_THREADING)
11560 #include <sched.h>
11561 #include <pthread.h> /* For pthreads. */
11562 #endif
11563
11564 #include <sys/time.h> /* select() (used for ma_sleep()). */
11565 #include <time.h> /* For nanosleep() */
11566 #include <unistd.h>
11567#endif
11568
11569#include <sys/stat.h> /* For fstat(), etc. */
11570
11571#ifdef MA_EMSCRIPTEN
11572#include <emscripten/emscripten.h>
11573#endif
11574
11575
11576/* Architecture Detection */
11577#if !defined(MA_64BIT) && !defined(MA_32BIT)
11578#ifdef _WIN32
11579#ifdef _WIN64
11580#define MA_64BIT
11581#else
11582#define MA_32BIT
11583#endif
11584#endif
11585#endif
11586
11587#if !defined(MA_64BIT) && !defined(MA_32BIT)
11588#ifdef __GNUC__
11589#ifdef __LP64__
11590#define MA_64BIT
11591#else
11592#define MA_32BIT
11593#endif
11594#endif
11595#endif
11596
11597#if !defined(MA_64BIT) && !defined(MA_32BIT)
11598#include <stdint.h>
11599#if INTPTR_MAX == INT64_MAX
11600#define MA_64BIT
11601#else
11602#define MA_32BIT
11603#endif
11604#endif
11605
11606#if defined(__arm__) || defined(_M_ARM)
11607#define MA_ARM32
11608#endif
11609#if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64)
11610#define MA_ARM64
11611#endif
11612
11613#if defined(__x86_64__) || defined(_M_X64)
11614#define MA_X64
11615#elif defined(__i386) || defined(_M_IX86)
11616#define MA_X86
11617#elif defined(MA_ARM32) || defined(MA_ARM64)
11618#define MA_ARM
11619#endif
11620
11621/* Intrinsics Support */
11622#if (defined(MA_X64) || defined(MA_X86)) && !defined(__COSMOPOLITAN__)
11623 #if defined(_MSC_VER) && !defined(__clang__)
11624 /* MSVC. */
11625 #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */
11626 #define MA_SUPPORT_SSE2
11627 #endif
11628 /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */
11629 /* #define MA_SUPPORT_AVX*/
11630 /*#endif*/
11631 #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */
11632 #define MA_SUPPORT_AVX2
11633 #endif
11634 #else
11635 /* Assume GNUC-style. */
11636 #if defined(__SSE2__) && !defined(MA_NO_SSE2)
11637 #define MA_SUPPORT_SSE2
11638 #endif
11639 /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/
11640 /* #define MA_SUPPORT_AVX*/
11641 /*#endif*/
11642 #if defined(__AVX2__) && !defined(MA_NO_AVX2)
11643 #define MA_SUPPORT_AVX2
11644 #endif
11645 #endif
11646
11647 /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */
11648 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
11649 #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include(<emmintrin.h>)
11650 #define MA_SUPPORT_SSE2
11651 #endif
11652 /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include(<immintrin.h>)*/
11653 /* #define MA_SUPPORT_AVX*/
11654 /*#endif*/
11655 #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include(<immintrin.h>)
11656 #define MA_SUPPORT_AVX2
11657 #endif
11658 #endif
11659
11660 #if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX)
11661 #include <immintrin.h>
11662 #elif defined(MA_SUPPORT_SSE2)
11663 #include <emmintrin.h>
11664 #endif
11665#endif
11666
11667#if defined(MA_ARM)
11668 #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
11669 #define MA_SUPPORT_NEON
11670 #include <arm_neon.h>
11671 #endif
11672#endif
11673
11674/* Begin globally disabled warnings. */
11675#if defined(_MSC_VER)
11676 #pragma warning(push)
11677 #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */
11678 #pragma warning(disable:4049) /* compiler limit : terminating line number emission */
11679#endif
11680
11681#if defined(MA_X64) || defined(MA_X86)
11682 #if defined(_MSC_VER) && !defined(__clang__)
11683 #if _MSC_VER >= 1400
11684 #include <intrin.h>
11685 static MA_INLINE void ma_cpuid(int info[4], int fid)
11686 {
11687 __cpuid(info, fid);
11688 }
11689 #else
11690 #define MA_NO_CPUID
11691 #endif
11692
11693 #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219)
11694 static MA_INLINE unsigned __int64 ma_xgetbv(int reg)
11695 {
11696 return _xgetbv(reg);
11697 }
11698 #else
11699 #define MA_NO_XGETBV
11700 #endif
11701 #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID)
11702 static MA_INLINE void ma_cpuid(int info[4], int fid)
11703 {
11704 /*
11705 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
11706 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
11707 supporting different assembly dialects.
11708
11709 What's basically happening is that we're saving and restoring the ebx register manually.
11710 */
11711 #if defined(MA_X86) && defined(__PIC__)
11712 __asm__ __volatile__ (
11713 "xchg{l} {%%}ebx, %k1;"
11714 "cpuid;"
11715 "xchg{l} {%%}ebx, %k1;"
11716 : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
11717 );
11718 #else
11719 __asm__ __volatile__ (
11720 "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
11721 );
11722 #endif
11723 }
11724
11725 static MA_INLINE ma_uint64 ma_xgetbv(int reg)
11726 {
11727 unsigned int hi;
11728 unsigned int lo;
11729
11730 __asm__ __volatile__ (
11731 "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg)
11732 );
11733
11734 return ((ma_uint64)hi << 32) | (ma_uint64)lo;
11735 }
11736 #else
11737 #define MA_NO_CPUID
11738 #define MA_NO_XGETBV
11739 #endif
11740#else
11741 #define MA_NO_CPUID
11742 #define MA_NO_XGETBV
11743#endif
11744
11745static MA_INLINE ma_bool32 ma_has_sse2(void)
11746{
11747#if defined(MA_SUPPORT_SSE2)
11748 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2)
11749 #if defined(MA_X64)
11750 return MA_TRUE; /* 64-bit targets always support SSE2. */
11751 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
11752 return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */
11753 #else
11754 #if defined(MA_NO_CPUID)
11755 return MA_FALSE;
11756 #else
11757 int info[4];
11758 ma_cpuid(info, 1);
11759 return (info[3] & (1 << 26)) != 0;
11760 #endif
11761 #endif
11762 #else
11763 return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */
11764 #endif
11765#else
11766 return MA_FALSE; /* No compiler support. */
11767#endif
11768}
11769
11770#if 0
11771static MA_INLINE ma_bool32 ma_has_avx()
11772{
11773#if defined(MA_SUPPORT_AVX)
11774 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX)
11775 #if defined(_AVX_) || defined(__AVX__)
11776 return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */
11777 #else
11778 /* AVX requires both CPU and OS support. */
11779 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
11780 return MA_FALSE;
11781 #else
11782 int info[4];
11783 ma_cpuid(info, 1);
11784 if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) {
11785 ma_uint64 xrc = ma_xgetbv(0);
11786 if ((xrc & 0x06) == 0x06) {
11787 return MA_TRUE;
11788 } else {
11789 return MA_FALSE;
11790 }
11791 } else {
11792 return MA_FALSE;
11793 }
11794 #endif
11795 #endif
11796 #else
11797 return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */
11798 #endif
11799#else
11800 return MA_FALSE; /* No compiler support. */
11801#endif
11802}
11803#endif
11804
11805static MA_INLINE ma_bool32 ma_has_avx2(void)
11806{
11807#if defined(MA_SUPPORT_AVX2)
11808 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2)
11809 #if defined(_AVX2_) || defined(__AVX2__)
11810 return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */
11811 #else
11812 /* AVX2 requires both CPU and OS support. */
11813 #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
11814 return MA_FALSE;
11815 #else
11816 int info1[4];
11817 int info7[4];
11818 ma_cpuid(info1, 1);
11819 ma_cpuid(info7, 7);
11820 if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) {
11821 ma_uint64 xrc = ma_xgetbv(0);
11822 if ((xrc & 0x06) == 0x06) {
11823 return MA_TRUE;
11824 } else {
11825 return MA_FALSE;
11826 }
11827 } else {
11828 return MA_FALSE;
11829 }
11830 #endif
11831 #endif
11832 #else
11833 return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */
11834 #endif
11835#else
11836 return MA_FALSE; /* No compiler support. */
11837#endif
11838}
11839
11840static MA_INLINE ma_bool32 ma_has_neon(void)
11841{
11842#if defined(MA_SUPPORT_NEON)
11843 #if defined(MA_ARM) && !defined(MA_NO_NEON)
11844 #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
11845 return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */
11846 #else
11847 /* TODO: Runtime check. */
11848 return MA_FALSE;
11849 #endif
11850 #else
11851 return MA_FALSE; /* NEON is only supported on ARM architectures. */
11852 #endif
11853#else
11854 return MA_FALSE; /* No compiler support. */
11855#endif
11856}
11857
11858#if defined(__has_builtin)
11859 #define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x)
11860#else
11861 #define MA_COMPILER_HAS_BUILTIN(x) 0
11862#endif
11863
11864#ifndef MA_ASSUME
11865 #if MA_COMPILER_HAS_BUILTIN(__builtin_assume)
11866 #define MA_ASSUME(x) __builtin_assume(x)
11867 #elif MA_COMPILER_HAS_BUILTIN(__builtin_unreachable)
11868 #define MA_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0)
11869 #elif defined(_MSC_VER)
11870 #define MA_ASSUME(x) __assume(x)
11871 #else
11872 #define MA_ASSUME(x) (void)(x)
11873 #endif
11874#endif
11875
11876#ifndef MA_RESTRICT
11877 #if defined(__clang__) || defined(_MSC_VER) || (defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)))
11878 #define MA_RESTRICT __restrict
11879 #else
11880 #define MA_RESTRICT
11881 #endif
11882#endif
11883
11884#if defined(_MSC_VER) && _MSC_VER >= 1400
11885 #define MA_HAS_BYTESWAP16_INTRINSIC
11886 #define MA_HAS_BYTESWAP32_INTRINSIC
11887 #define MA_HAS_BYTESWAP64_INTRINSIC
11888#elif defined(__clang__)
11889 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap16)
11890 #define MA_HAS_BYTESWAP16_INTRINSIC
11891 #endif
11892 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap32)
11893 #define MA_HAS_BYTESWAP32_INTRINSIC
11894 #endif
11895 #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap64)
11896 #define MA_HAS_BYTESWAP64_INTRINSIC
11897 #endif
11898#elif defined(__GNUC__)
11899 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
11900 #define MA_HAS_BYTESWAP32_INTRINSIC
11901 #define MA_HAS_BYTESWAP64_INTRINSIC
11902 #endif
11903 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
11904 #define MA_HAS_BYTESWAP16_INTRINSIC
11905 #endif
11906#endif
11907
11908
11909static MA_INLINE ma_bool32 ma_is_little_endian(void)
11910{
11911#if defined(MA_X86) || defined(MA_X64)
11912 return MA_TRUE;
11913#else
11914 int n = 1;
11915 return (*(char*)&n) == 1;
11916#endif
11917}
11918
11919static MA_INLINE ma_bool32 ma_is_big_endian(void)
11920{
11921 return !ma_is_little_endian();
11922}
11923
11924
11925static MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n)
11926{
11927#ifdef MA_HAS_BYTESWAP32_INTRINSIC
11928 #if defined(_MSC_VER)
11929 return _byteswap_ulong(n);
11930 #elif defined(__GNUC__) || defined(__clang__)
11931 #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. */
11932 /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */
11933 ma_uint32 r;
11934 __asm__ __volatile__ (
11935 #if defined(MA_64BIT)
11936 "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! */
11937 #else
11938 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
11939 #endif
11940 );
11941 return r;
11942 #else
11943 return __builtin_bswap32(n);
11944 #endif
11945 #else
11946 #error "This compiler does not support the byte swap intrinsic."
11947 #endif
11948#else
11949 return ((n & 0xFF000000) >> 24) |
11950 ((n & 0x00FF0000) >> 8) |
11951 ((n & 0x0000FF00) << 8) |
11952 ((n & 0x000000FF) << 24);
11953#endif
11954}
11955
11956
11957#if !defined(MA_EMSCRIPTEN)
11958#ifdef MA_WIN32
11959static void ma_sleep__win32(ma_uint32 milliseconds)
11960{
11961 Sleep((DWORD)milliseconds);
11962}
11963#endif
11964#ifdef MA_POSIX
11965static void ma_sleep__posix(ma_uint32 milliseconds)
11966{
11967#ifdef MA_EMSCRIPTEN
11968 (void)milliseconds;
11969 MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */
11970#else
11971 #if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || defined(MA_NX)
11972 struct timespec ts;
11973 ts.tv_sec = milliseconds / 1000;
11974 ts.tv_nsec = milliseconds % 1000 * 1000000;
11975 nanosleep(&ts, NULL);
11976 #else
11977 struct timeval tv;
11978 tv.tv_sec = milliseconds / 1000;
11979 tv.tv_usec = milliseconds % 1000 * 1000;
11980 select(0, NULL, NULL, NULL, &tv);
11981 #endif
11982#endif
11983}
11984#endif
11985
11986static MA_INLINE void ma_sleep(ma_uint32 milliseconds)
11987{
11988#ifdef MA_WIN32
11989 ma_sleep__win32(milliseconds);
11990#endif
11991#ifdef MA_POSIX
11992 ma_sleep__posix(milliseconds);
11993#endif
11994}
11995#endif
11996
11997static MA_INLINE void ma_yield(void)
11998{
11999#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
12000 /* x86/x64 */
12001 #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__)
12002 #if _MSC_VER >= 1400
12003 _mm_pause();
12004 #else
12005 #if defined(__DMC__)
12006 /* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */
12007 __asm nop;
12008 #else
12009 __asm pause;
12010 #endif
12011 #endif
12012 #else
12013 __asm__ __volatile__ ("rep; nop");
12014 #endif
12015#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__)
12016 /* ARM */
12017 #if defined(_MSC_VER)
12018 /* 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. */
12019 __yield();
12020 #else
12021 __asm__ __volatile__ ("yield"); /* ARMv6K/ARMv6T2 and above. */
12022 #endif
12023#else
12024 /* Unknown or unsupported architecture. No-op. */
12025#endif
12026}
12027
12028
12029#define MA_MM_DENORMALS_ZERO_MASK 0x0040
12030#define MA_MM_FLUSH_ZERO_MASK 0x8000
12031
12032static MA_INLINE unsigned int ma_disable_denormals(void)
12033{
12034 unsigned int prevState;
12035
12036 #if defined(_MSC_VER)
12037 {
12038 /*
12039 Older versions of Visual Studio don't support the "safe" versions of _controlfp_s(). I don't
12040 know which version of Visual Studio first added support for _controlfp_s(), but I do know
12041 that VC6 lacks support. _MSC_VER = 1200 is VC6, but if you get compilation errors on older
12042 versions of Visual Studio, let me know and I'll make the necessary adjustment.
12043 */
12044 #if _MSC_VER <= 1200
12045 {
12046 prevState = _statusfp();
12047 _controlfp(prevState | _DN_FLUSH, _MCW_DN);
12048 }
12049 #else
12050 {
12051 unsigned int unused;
12052 _controlfp_s(&prevState, 0, 0);
12053 _controlfp_s(&unused, prevState | _DN_FLUSH, _MCW_DN);
12054 }
12055 #endif
12056 }
12057 #elif defined(MA_X86) || defined(MA_X64)
12058 {
12059 #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */
12060 {
12061 prevState = _mm_getcsr();
12062 _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK);
12063 }
12064 #else
12065 {
12066 /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */
12067 prevState = 0;
12068 }
12069 #endif
12070 }
12071 #else
12072 {
12073 /* Unknown or unsupported architecture. No-op. */
12074 prevState = 0;
12075 }
12076 #endif
12077
12078 return prevState;
12079}
12080
12081static MA_INLINE void ma_restore_denormals(unsigned int prevState)
12082{
12083 #if defined(_MSC_VER)
12084 {
12085 /* Older versions of Visual Studio do not support _controlfp_s(). See ma_disable_denormals(). */
12086 #if _MSC_VER <= 1200
12087 {
12088 _controlfp(prevState, _MCW_DN);
12089 }
12090 #else
12091 {
12092 unsigned int unused;
12093 _controlfp_s(&unused, prevState, _MCW_DN);
12094 }
12095 #endif
12096 }
12097 #elif defined(MA_X86) || defined(MA_X64)
12098 {
12099 #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */
12100 {
12101 _mm_setcsr(prevState);
12102 }
12103 #else
12104 {
12105 /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */
12106 (void)prevState;
12107 }
12108 #endif
12109 }
12110 #else
12111 {
12112 /* Unknown or unsupported architecture. No-op. */
12113 (void)prevState;
12114 }
12115 #endif
12116}
12117
12118
12119#ifdef MA_ANDROID
12120#include <sys/system_properties.h>
12121
12122int ma_android_sdk_version()
12123{
12124 char sdkVersion[PROP_VALUE_MAX + 1] = {0, };
12125 if (__system_property_get("ro.build.version.sdk", sdkVersion)) {
12126 return atoi(sdkVersion);
12127 }
12128
12129 return 0;
12130}
12131#endif
12132
12133
12134#ifndef MA_COINIT_VALUE
12135#define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */
12136#endif
12137
12138
12139#ifndef MA_FLT_MAX
12140 #ifdef FLT_MAX
12141 #define MA_FLT_MAX FLT_MAX
12142 #else
12143 #define MA_FLT_MAX 3.402823466e+38F
12144 #endif
12145#endif
12146
12147
12148#ifndef MA_PI
12149#define MA_PI 3.14159265358979323846264f
12150#endif
12151#ifndef MA_PI_D
12152#define MA_PI_D 3.14159265358979323846264
12153#endif
12154#ifndef MA_TAU
12155#define MA_TAU 6.28318530717958647693f
12156#endif
12157#ifndef MA_TAU_D
12158#define MA_TAU_D 6.28318530717958647693
12159#endif
12160
12161
12162/* The default format when ma_format_unknown (0) is requested when initializing a device. */
12163#ifndef MA_DEFAULT_FORMAT
12164#define MA_DEFAULT_FORMAT ma_format_f32
12165#endif
12166
12167/* The default channel count to use when 0 is used when initializing a device. */
12168#ifndef MA_DEFAULT_CHANNELS
12169#define MA_DEFAULT_CHANNELS 2
12170#endif
12171
12172/* The default sample rate to use when 0 is used when initializing a device. */
12173#ifndef MA_DEFAULT_SAMPLE_RATE
12174#define MA_DEFAULT_SAMPLE_RATE 48000
12175#endif
12176
12177/* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */
12178#ifndef MA_DEFAULT_PERIODS
12179#define MA_DEFAULT_PERIODS 3
12180#endif
12181
12182/* The default period size in milliseconds for low latency mode. */
12183#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY
12184#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10
12185#endif
12186
12187/* The default buffer size in milliseconds for conservative mode. */
12188#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE
12189#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100
12190#endif
12191
12192/* The default LPF filter order for linear resampling. Note that this is clamped to MA_MAX_FILTER_ORDER. */
12193#ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER
12194 #if MA_MAX_FILTER_ORDER >= 4
12195 #define MA_DEFAULT_RESAMPLER_LPF_ORDER 4
12196 #else
12197 #define MA_DEFAULT_RESAMPLER_LPF_ORDER MA_MAX_FILTER_ORDER
12198 #endif
12199#endif
12200
12201
12202#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
12203 #pragma GCC diagnostic push
12204 #pragma GCC diagnostic ignored "-Wunused-variable"
12205#endif
12206
12207/* Standard sample rates, in order of priority. */
12208static ma_uint32 g_maStandardSampleRatePriorities[] = {
12211
12215
12220
12224
12227};
12228
12229static MA_INLINE ma_bool32 ma_is_standard_sample_rate(ma_uint32 sampleRate)
12230{
12231 ma_uint32 iSampleRate;
12232
12233 for (iSampleRate = 0; iSampleRate < sizeof(g_maStandardSampleRatePriorities) / sizeof(g_maStandardSampleRatePriorities[0]); iSampleRate += 1) {
12234 if (g_maStandardSampleRatePriorities[iSampleRate] == sampleRate) {
12235 return MA_TRUE;
12236 }
12237 }
12238
12239 /* Getting here means the sample rate is not supported. */
12240 return MA_FALSE;
12241}
12242
12243
12244static ma_format g_maFormatPriorities[] = {
12245 ma_format_s16, /* Most common */
12247
12248 /*ma_format_s24_32,*/ /* Clean alignment */
12250
12251 ma_format_s24, /* Unclean alignment */
12252
12253 ma_format_u8 /* Low quality */
12254};
12255#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
12256 #pragma GCC diagnostic pop
12257#endif
12258
12259
12260MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)
12261{
12262 if (pMajor) {
12263 *pMajor = MA_VERSION_MAJOR;
12264 }
12265
12266 if (pMinor) {
12267 *pMinor = MA_VERSION_MINOR;
12268 }
12269
12270 if (pRevision) {
12271 *pRevision = MA_VERSION_REVISION;
12272 }
12273}
12274
12275MA_API const char* ma_version_string(void)
12276{
12277 return MA_VERSION_STRING;
12278}
12279
12280
12281/******************************************************************************
12282
12283Standard Library Stuff
12284
12285******************************************************************************/
12286#ifndef MA_ASSERT
12287#define MA_ASSERT(condition) assert(condition)
12288#endif
12289
12290#ifndef MA_MALLOC
12291#define MA_MALLOC(sz) malloc((sz))
12292#endif
12293#ifndef MA_REALLOC
12294#define MA_REALLOC(p, sz) realloc((p), (sz))
12295#endif
12296#ifndef MA_FREE
12297#define MA_FREE(p) free((p))
12298#endif
12299
12300static MA_INLINE void ma_zero_memory_default(void* p, size_t sz)
12301{
12302 if (p == NULL) {
12303 MA_ASSERT(sz == 0); /* If this is triggered there's an error with the calling code. */
12304 return;
12305 }
12306
12307 if (sz > 0) {
12308 memset(p, 0, sz);
12309 }
12310}
12311
12312
12313#ifndef MA_ZERO_MEMORY
12314#define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz))
12315#endif
12316#ifndef MA_COPY_MEMORY
12317#define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
12318#endif
12319#ifndef MA_MOVE_MEMORY
12320#define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz))
12321#endif
12322
12323#define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p)))
12324
12325#define ma_countof(x) (sizeof(x) / sizeof(x[0]))
12326#define ma_max(x, y) (((x) > (y)) ? (x) : (y))
12327#define ma_min(x, y) (((x) < (y)) ? (x) : (y))
12328#define ma_abs(x) (((x) > 0) ? (x) : -(x))
12329#define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi)))
12330#define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset))
12331#define ma_align(x, a) (((x) + ((a)-1)) & ~((a)-1))
12332#define ma_align_64(x) ma_align(x, 8)
12333
12334#define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels))
12335
12336static MA_INLINE double ma_sind(double x)
12337{
12338 /* TODO: Implement custom sin(x). */
12339 return sin(x);
12340}
12341
12342static MA_INLINE double ma_expd(double x)
12343{
12344 /* TODO: Implement custom exp(x). */
12345 return exp(x);
12346}
12347
12348static MA_INLINE double ma_logd(double x)
12349{
12350 /* TODO: Implement custom log(x). */
12351 return log(x);
12352}
12353
12354static MA_INLINE double ma_powd(double x, double y)
12355{
12356 /* TODO: Implement custom pow(x, y). */
12357 return pow(x, y);
12358}
12359
12360static MA_INLINE double ma_sqrtd(double x)
12361{
12362 /* TODO: Implement custom sqrt(x). */
12363 return sqrt(x);
12364}
12365
12366
12367static MA_INLINE float ma_rsqrtf(float x)
12368{
12369 #if defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && (defined(MA_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))
12370 {
12371 /*
12372 For SSE we can use RSQRTSS.
12373
12374 This Stack Overflow post suggests that compilers don't necessarily generate optimal code
12375 when using intrinsics:
12376
12377 https://web.archive.org/web/20221211012522/https://stackoverflow.com/questions/32687079/getting-fewest-instructions-for-rsqrtss-wrapper
12378
12379 I'm going to do something similar here, but a bit simpler.
12380 */
12381 #if defined(__GNUC__) || defined(__clang__)
12382 {
12383 float result;
12384 __asm__ __volatile__("rsqrtss %1, %0" : "=x"(result) : "x"(x));
12385 return result;
12386 }
12387 #else
12388 {
12389 return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ps1(x)));
12390 }
12391 #endif
12392 }
12393 #else
12394 {
12395 return 1 / (float)ma_sqrtd(x);
12396 }
12397 #endif
12398}
12399
12400
12401static MA_INLINE float ma_sinf(float x)
12402{
12403 return (float)ma_sind((float)x);
12404}
12405
12406static MA_INLINE double ma_cosd(double x)
12407{
12408 return ma_sind((MA_PI_D*0.5) - x);
12409}
12410
12411static MA_INLINE float ma_cosf(float x)
12412{
12413 return (float)ma_cosd((float)x);
12414}
12415
12416static MA_INLINE double ma_log10d(double x)
12417{
12418 return ma_logd(x) * 0.43429448190325182765;
12419}
12420
12421static MA_INLINE float ma_powf(float x, float y)
12422{
12423 return (float)ma_powd((double)x, (double)y);
12424}
12425
12426static MA_INLINE float ma_log10f(float x)
12427{
12428 return (float)ma_log10d((double)x);
12429}
12430
12431
12432static MA_INLINE double ma_degrees_to_radians(double degrees)
12433{
12434 return degrees * 0.01745329252;
12435}
12436
12437static MA_INLINE double ma_radians_to_degrees(double radians)
12438{
12439 return radians * 57.295779512896;
12440}
12441
12442static MA_INLINE float ma_degrees_to_radians_f(float degrees)
12443{
12444 return degrees * 0.01745329252f;
12445}
12446
12447static MA_INLINE float ma_radians_to_degrees_f(float radians)
12448{
12449 return radians * 57.295779512896f;
12450}
12451
12452
12453/*
12454Return Values:
12455 0: Success
12456 22: EINVAL
12457 34: ERANGE
12458
12459Not using symbolic constants for errors because I want to avoid #including errno.h
12460
12461These are marked as no-inline because of some bad code generation by Clang. None of these functions
12462are used in any performance-critical code within miniaudio.
12463*/
12464MA_API MA_NO_INLINE int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
12465{
12466 size_t i;
12467
12468 if (dst == 0) {
12469 return 22;
12470 }
12471 if (dstSizeInBytes == 0) {
12472 return 34;
12473 }
12474 if (src == 0) {
12475 dst[0] = '\0';
12476 return 22;
12477 }
12478
12479 for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
12480 dst[i] = src[i];
12481 }
12482
12483 if (i < dstSizeInBytes) {
12484 dst[i] = '\0';
12485 return 0;
12486 }
12487
12488 dst[0] = '\0';
12489 return 34;
12490}
12491
12492MA_API MA_NO_INLINE int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src)
12493{
12494 size_t i;
12495
12496 if (dst == 0) {
12497 return 22;
12498 }
12499 if (dstCap == 0) {
12500 return 34;
12501 }
12502 if (src == 0) {
12503 dst[0] = '\0';
12504 return 22;
12505 }
12506
12507 for (i = 0; i < dstCap && src[i] != '\0'; ++i) {
12508 dst[i] = src[i];
12509 }
12510
12511 if (i < dstCap) {
12512 dst[i] = '\0';
12513 return 0;
12514 }
12515
12516 dst[0] = '\0';
12517 return 34;
12518}
12519
12520
12521MA_API MA_NO_INLINE int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
12522{
12523 size_t maxcount;
12524 size_t i;
12525
12526 if (dst == 0) {
12527 return 22;
12528 }
12529 if (dstSizeInBytes == 0) {
12530 return 34;
12531 }
12532 if (src == 0) {
12533 dst[0] = '\0';
12534 return 22;
12535 }
12536
12537 maxcount = count;
12538 if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */
12539 maxcount = dstSizeInBytes - 1;
12540 }
12541
12542 for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
12543 dst[i] = src[i];
12544 }
12545
12546 if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
12547 dst[i] = '\0';
12548 return 0;
12549 }
12550
12551 dst[0] = '\0';
12552 return 34;
12553}
12554
12555MA_API MA_NO_INLINE int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
12556{
12557 char* dstorig;
12558
12559 if (dst == 0) {
12560 return 22;
12561 }
12562 if (dstSizeInBytes == 0) {
12563 return 34;
12564 }
12565 if (src == 0) {
12566 dst[0] = '\0';
12567 return 22;
12568 }
12569
12570 dstorig = dst;
12571
12572 while (dstSizeInBytes > 0 && dst[0] != '\0') {
12573 dst += 1;
12574 dstSizeInBytes -= 1;
12575 }
12576
12577 if (dstSizeInBytes == 0) {
12578 return 22; /* Unterminated. */
12579 }
12580
12581
12582 while (dstSizeInBytes > 0 && src[0] != '\0') {
12583 *dst++ = *src++;
12584 dstSizeInBytes -= 1;
12585 }
12586
12587 if (dstSizeInBytes > 0) {
12588 dst[0] = '\0';
12589 } else {
12590 dstorig[0] = '\0';
12591 return 34;
12592 }
12593
12594 return 0;
12595}
12596
12597MA_API MA_NO_INLINE int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
12598{
12599 char* dstorig;
12600
12601 if (dst == 0) {
12602 return 22;
12603 }
12604 if (dstSizeInBytes == 0) {
12605 return 34;
12606 }
12607 if (src == 0) {
12608 return 22;
12609 }
12610
12611 dstorig = dst;
12612
12613 while (dstSizeInBytes > 0 && dst[0] != '\0') {
12614 dst += 1;
12615 dstSizeInBytes -= 1;
12616 }
12617
12618 if (dstSizeInBytes == 0) {
12619 return 22; /* Unterminated. */
12620 }
12621
12622
12623 if (count == ((size_t)-1)) { /* _TRUNCATE */
12624 count = dstSizeInBytes - 1;
12625 }
12626
12627 while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) {
12628 *dst++ = *src++;
12629 dstSizeInBytes -= 1;
12630 count -= 1;
12631 }
12632
12633 if (dstSizeInBytes > 0) {
12634 dst[0] = '\0';
12635 } else {
12636 dstorig[0] = '\0';
12637 return 34;
12638 }
12639
12640 return 0;
12641}
12642
12643MA_API MA_NO_INLINE int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix)
12644{
12645 int sign;
12646 unsigned int valueU;
12647 char* dstEnd;
12648
12649 if (dst == NULL || dstSizeInBytes == 0) {
12650 return 22;
12651 }
12652 if (radix < 2 || radix > 36) {
12653 dst[0] = '\0';
12654 return 22;
12655 }
12656
12657 sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */
12658
12659 if (value < 0) {
12660 valueU = -value;
12661 } else {
12662 valueU = value;
12663 }
12664
12665 dstEnd = dst;
12666 do
12667 {
12668 int remainder = valueU % radix;
12669 if (remainder > 9) {
12670 *dstEnd = (char)((remainder - 10) + 'a');
12671 } else {
12672 *dstEnd = (char)(remainder + '0');
12673 }
12674
12675 dstEnd += 1;
12676 dstSizeInBytes -= 1;
12677 valueU /= radix;
12678 } while (dstSizeInBytes > 0 && valueU > 0);
12679
12680 if (dstSizeInBytes == 0) {
12681 dst[0] = '\0';
12682 return 22; /* Ran out of room in the output buffer. */
12683 }
12684
12685 if (sign < 0) {
12686 *dstEnd++ = '-';
12687 dstSizeInBytes -= 1;
12688 }
12689
12690 if (dstSizeInBytes == 0) {
12691 dst[0] = '\0';
12692 return 22; /* Ran out of room in the output buffer. */
12693 }
12694
12695 *dstEnd = '\0';
12696
12697
12698 /* At this point the string will be reversed. */
12699 dstEnd -= 1;
12700 while (dst < dstEnd) {
12701 char temp = *dst;
12702 *dst = *dstEnd;
12703 *dstEnd = temp;
12704
12705 dst += 1;
12706 dstEnd -= 1;
12707 }
12708
12709 return 0;
12710}
12711
12712MA_API MA_NO_INLINE int ma_strcmp(const char* str1, const char* str2)
12713{
12714 if (str1 == str2) return 0;
12715
12716 /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */
12717 if (str1 == NULL) return -1;
12718 if (str2 == NULL) return 1;
12719
12720 for (;;) {
12721 if (str1[0] == '\0') {
12722 break;
12723 }
12724 if (str1[0] != str2[0]) {
12725 break;
12726 }
12727
12728 str1 += 1;
12729 str2 += 1;
12730 }
12731
12732 return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0];
12733}
12734
12735MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB)
12736{
12737 int result;
12738
12739 result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1);
12740 if (result != 0) {
12741 return result;
12742 }
12743
12744 result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1);
12745 if (result != 0) {
12746 return result;
12747 }
12748
12749 return result;
12750}
12751
12752MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks)
12753{
12754 size_t sz;
12755 char* dst;
12756
12757 if (src == NULL) {
12758 return NULL;
12759 }
12760
12761 sz = strlen(src)+1;
12762 dst = (char*)ma_malloc(sz, pAllocationCallbacks);
12763 if (dst == NULL) {
12764 return NULL;
12765 }
12766
12767 ma_strcpy_s(dst, sz, src);
12768
12769 return dst;
12770}
12771
12772MA_API MA_NO_INLINE wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks)
12773{
12774 size_t sz = wcslen(src)+1;
12775 wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks);
12776 if (dst == NULL) {
12777 return NULL;
12778 }
12779
12780 ma_wcscpy_s(dst, sz, src);
12781
12782 return dst;
12783}
12784
12785
12786
12787#include <errno.h>
12788static ma_result ma_result_from_errno(int e)
12789{
12790 if (e == 0) {
12791 return MA_SUCCESS;
12792 }
12793#ifdef EPERM
12794 else if (e == EPERM) { return MA_INVALID_OPERATION; }
12795#endif
12796#ifdef ENOENT
12797 else if (e == ENOENT) { return MA_DOES_NOT_EXIST; }
12798#endif
12799#ifdef ESRCH
12800 else if (e == ESRCH) { return MA_DOES_NOT_EXIST; }
12801#endif
12802#ifdef EINTR
12803 else if (e == EINTR) { return MA_INTERRUPT; }
12804#endif
12805#ifdef EIO
12806 else if (e == EIO) { return MA_IO_ERROR; }
12807#endif
12808#ifdef ENXIO
12809 else if (e == ENXIO) { return MA_DOES_NOT_EXIST; }
12810#endif
12811#ifdef E2BIG
12812 else if (e == E2BIG) { return MA_INVALID_ARGS; }
12813#endif
12814#ifdef ENOEXEC
12815 else if (e == ENOEXEC) { return MA_INVALID_FILE; }
12816#endif
12817#ifdef EBADF
12818 else if (e == EBADF) { return MA_INVALID_FILE; }
12819#endif
12820#ifdef ECHILD
12821 else if (e == ECHILD) { return MA_ERROR; }
12822#endif
12823#ifdef EAGAIN
12824 else if (e == EAGAIN) { return MA_UNAVAILABLE; }
12825#endif
12826#ifdef ENOMEM
12827 else if (e == ENOMEM) { return MA_OUT_OF_MEMORY; }
12828#endif
12829#ifdef EACCES
12830 else if (e == EACCES) { return MA_ACCESS_DENIED; }
12831#endif
12832#ifdef EFAULT
12833 else if (e == EFAULT) { return MA_BAD_ADDRESS; }
12834#endif
12835#ifdef ENOTBLK
12836 else if (e == ENOTBLK) { return MA_ERROR; }
12837#endif
12838#ifdef EBUSY
12839 else if (e == EBUSY) { return MA_BUSY; }
12840#endif
12841#ifdef EEXIST
12842 else if (e == EEXIST) { return MA_ALREADY_EXISTS; }
12843#endif
12844#ifdef EXDEV
12845 else if (e == EXDEV) { return MA_ERROR; }
12846#endif
12847#ifdef ENODEV
12848 else if (e == ENODEV) { return MA_DOES_NOT_EXIST; }
12849#endif
12850#ifdef ENOTDIR
12851 else if (e == ENOTDIR) { return MA_NOT_DIRECTORY; }
12852#endif
12853#ifdef EISDIR
12854 else if (e == EISDIR) { return MA_IS_DIRECTORY; }
12855#endif
12856#ifdef EINVAL
12857 else if (e == EINVAL) { return MA_INVALID_ARGS; }
12858#endif
12859#ifdef ENFILE
12860 else if (e == ENFILE) { return MA_TOO_MANY_OPEN_FILES; }
12861#endif
12862#ifdef EMFILE
12863 else if (e == EMFILE) { return MA_TOO_MANY_OPEN_FILES; }
12864#endif
12865#ifdef ENOTTY
12866 else if (e == ENOTTY) { return MA_INVALID_OPERATION; }
12867#endif
12868#ifdef ETXTBSY
12869 else if (e == ETXTBSY) { return MA_BUSY; }
12870#endif
12871#ifdef EFBIG
12872 else if (e == EFBIG) { return MA_TOO_BIG; }
12873#endif
12874#ifdef ENOSPC
12875 else if (e == ENOSPC) { return MA_NO_SPACE; }
12876#endif
12877#ifdef ESPIPE
12878 else if (e == ESPIPE) { return MA_BAD_SEEK; }
12879#endif
12880#ifdef EROFS
12881 else if (e == EROFS) { return MA_ACCESS_DENIED; }
12882#endif
12883#ifdef EMLINK
12884 else if (e == EMLINK) { return MA_TOO_MANY_LINKS; }
12885#endif
12886#ifdef EPIPE
12887 else if (e == EPIPE) { return MA_BAD_PIPE; }
12888#endif
12889#ifdef EDOM
12890 else if (e == EDOM) { return MA_OUT_OF_RANGE; }
12891#endif
12892#ifdef ERANGE
12893 else if (e == ERANGE) { return MA_OUT_OF_RANGE; }
12894#endif
12895#ifdef EDEADLK
12896 else if (e == EDEADLK) { return MA_DEADLOCK; }
12897#endif
12898#ifdef ENAMETOOLONG
12899 else if (e == ENAMETOOLONG) { return MA_PATH_TOO_LONG; }
12900#endif
12901#ifdef ENOLCK
12902 else if (e == ENOLCK) { return MA_ERROR; }
12903#endif
12904#ifdef ENOSYS
12905 else if (e == ENOSYS) { return MA_NOT_IMPLEMENTED; }
12906#endif
12907#ifdef ENOTEMPTY
12908 else if (e == ENOTEMPTY) { return MA_DIRECTORY_NOT_EMPTY; }
12909#endif
12910#ifdef ELOOP
12911 else if (e == ELOOP) { return MA_TOO_MANY_LINKS; }
12912#endif
12913#ifdef ENOMSG
12914 else if (e == ENOMSG) { return MA_NO_MESSAGE; }
12915#endif
12916#ifdef EIDRM
12917 else if (e == EIDRM) { return MA_ERROR; }
12918#endif
12919#ifdef ECHRNG
12920 else if (e == ECHRNG) { return MA_ERROR; }
12921#endif
12922#ifdef EL2NSYNC
12923 else if (e == EL2NSYNC) { return MA_ERROR; }
12924#endif
12925#ifdef EL3HLT
12926 else if (e == EL3HLT) { return MA_ERROR; }
12927#endif
12928#ifdef EL3RST
12929 else if (e == EL3RST) { return MA_ERROR; }
12930#endif
12931#ifdef ELNRNG
12932 else if (e == ELNRNG) { return MA_OUT_OF_RANGE; }
12933#endif
12934#ifdef EUNATCH
12935 else if (e == EUNATCH) { return MA_ERROR; }
12936#endif
12937#ifdef ENOCSI
12938 else if (e == ENOCSI) { return MA_ERROR; }
12939#endif
12940#ifdef EL2HLT
12941 else if (e == EL2HLT) { return MA_ERROR; }
12942#endif
12943#ifdef EBADE
12944 else if (e == EBADE) { return MA_ERROR; }
12945#endif
12946#ifdef EBADR
12947 else if (e == EBADR) { return MA_ERROR; }
12948#endif
12949#ifdef EXFULL
12950 else if (e == EXFULL) { return MA_ERROR; }
12951#endif
12952#ifdef ENOANO
12953 else if (e == ENOANO) { return MA_ERROR; }
12954#endif
12955#ifdef EBADRQC
12956 else if (e == EBADRQC) { return MA_ERROR; }
12957#endif
12958#ifdef EBADSLT
12959 else if (e == EBADSLT) { return MA_ERROR; }
12960#endif
12961#ifdef EBFONT
12962 else if (e == EBFONT) { return MA_INVALID_FILE; }
12963#endif
12964#ifdef ENOSTR
12965 else if (e == ENOSTR) { return MA_ERROR; }
12966#endif
12967#ifdef ENODATA
12968 else if (e == ENODATA) { return MA_NO_DATA_AVAILABLE; }
12969#endif
12970#ifdef ETIME
12971 else if (e == ETIME) { return MA_TIMEOUT; }
12972#endif
12973#ifdef ENOSR
12974 else if (e == ENOSR) { return MA_NO_DATA_AVAILABLE; }
12975#endif
12976#ifdef ENONET
12977 else if (e == ENONET) { return MA_NO_NETWORK; }
12978#endif
12979#ifdef ENOPKG
12980 else if (e == ENOPKG) { return MA_ERROR; }
12981#endif
12982#ifdef EREMOTE
12983 else if (e == EREMOTE) { return MA_ERROR; }
12984#endif
12985#ifdef ENOLINK
12986 else if (e == ENOLINK) { return MA_ERROR; }
12987#endif
12988#ifdef EADV
12989 else if (e == EADV) { return MA_ERROR; }
12990#endif
12991#ifdef ESRMNT
12992 else if (e == ESRMNT) { return MA_ERROR; }
12993#endif
12994#ifdef ECOMM
12995 else if (e == ECOMM) { return MA_ERROR; }
12996#endif
12997#ifdef EPROTO
12998 else if (e == EPROTO) { return MA_ERROR; }
12999#endif
13000#ifdef EMULTIHOP
13001 else if (e == EMULTIHOP) { return MA_ERROR; }
13002#endif
13003#ifdef EDOTDOT
13004 else if (e == EDOTDOT) { return MA_ERROR; }
13005#endif
13006#ifdef EBADMSG
13007 else if (e == EBADMSG) { return MA_BAD_MESSAGE; }
13008#endif
13009#ifdef EOVERFLOW
13010 else if (e == EOVERFLOW) { return MA_TOO_BIG; }
13011#endif
13012#ifdef ENOTUNIQ
13013 else if (e == ENOTUNIQ) { return MA_NOT_UNIQUE; }
13014#endif
13015#ifdef EBADFD
13016 else if (e == EBADFD) { return MA_ERROR; }
13017#endif
13018#ifdef EREMCHG
13019 else if (e == EREMCHG) { return MA_ERROR; }
13020#endif
13021#ifdef ELIBACC
13022 else if (e == ELIBACC) { return MA_ACCESS_DENIED; }
13023#endif
13024#ifdef ELIBBAD
13025 else if (e == ELIBBAD) { return MA_INVALID_FILE; }
13026#endif
13027#ifdef ELIBSCN
13028 else if (e == ELIBSCN) { return MA_INVALID_FILE; }
13029#endif
13030#ifdef ELIBMAX
13031 else if (e == ELIBMAX) { return MA_ERROR; }
13032#endif
13033#ifdef ELIBEXEC
13034 else if (e == ELIBEXEC) { return MA_ERROR; }
13035#endif
13036#ifdef EILSEQ
13037 else if (e == EILSEQ) { return MA_INVALID_DATA; }
13038#endif
13039#ifdef ERESTART
13040 else if (e == ERESTART) { return MA_ERROR; }
13041#endif
13042#ifdef ESTRPIPE
13043 else if (e == ESTRPIPE) { return MA_ERROR; }
13044#endif
13045#ifdef EUSERS
13046 else if (e == EUSERS) { return MA_ERROR; }
13047#endif
13048#ifdef ENOTSOCK
13049 else if (e == ENOTSOCK) { return MA_NOT_SOCKET; }
13050#endif
13051#ifdef EDESTADDRREQ
13052 else if (e == EDESTADDRREQ) { return MA_NO_ADDRESS; }
13053#endif
13054#ifdef EMSGSIZE
13055 else if (e == EMSGSIZE) { return MA_TOO_BIG; }
13056#endif
13057#ifdef EPROTOTYPE
13058 else if (e == EPROTOTYPE) { return MA_BAD_PROTOCOL; }
13059#endif
13060#ifdef ENOPROTOOPT
13061 else if (e == ENOPROTOOPT) { return MA_PROTOCOL_UNAVAILABLE; }
13062#endif
13063#ifdef EPROTONOSUPPORT
13064 else if (e == EPROTONOSUPPORT) { return MA_PROTOCOL_NOT_SUPPORTED; }
13065#endif
13066#ifdef ESOCKTNOSUPPORT
13067 else if (e == ESOCKTNOSUPPORT) { return MA_SOCKET_NOT_SUPPORTED; }
13068#endif
13069#ifdef EOPNOTSUPP
13070 else if (e == EOPNOTSUPP) { return MA_INVALID_OPERATION; }
13071#endif
13072#ifdef EPFNOSUPPORT
13073 else if (e == EPFNOSUPPORT) { return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; }
13074#endif
13075#ifdef EAFNOSUPPORT
13076 else if (e == EAFNOSUPPORT) { return MA_ADDRESS_FAMILY_NOT_SUPPORTED; }
13077#endif
13078#ifdef EADDRINUSE
13079 else if (e == EADDRINUSE) { return MA_ALREADY_IN_USE; }
13080#endif
13081#ifdef EADDRNOTAVAIL
13082 else if (e == EADDRNOTAVAIL) { return MA_ERROR; }
13083#endif
13084#ifdef ENETDOWN
13085 else if (e == ENETDOWN) { return MA_NO_NETWORK; }
13086#endif
13087#ifdef ENETUNREACH
13088 else if (e == ENETUNREACH) { return MA_NO_NETWORK; }
13089#endif
13090#ifdef ENETRESET
13091 else if (e == ENETRESET) { return MA_NO_NETWORK; }
13092#endif
13093#ifdef ECONNABORTED
13094 else if (e == ECONNABORTED) { return MA_NO_NETWORK; }
13095#endif
13096#ifdef ECONNRESET
13097 else if (e == ECONNRESET) { return MA_CONNECTION_RESET; }
13098#endif
13099#ifdef ENOBUFS
13100 else if (e == ENOBUFS) { return MA_NO_SPACE; }
13101#endif
13102#ifdef EISCONN
13103 else if (e == EISCONN) { return MA_ALREADY_CONNECTED; }
13104#endif
13105#ifdef ENOTCONN
13106 else if (e == ENOTCONN) { return MA_NOT_CONNECTED; }
13107#endif
13108#ifdef ESHUTDOWN
13109 else if (e == ESHUTDOWN) { return MA_ERROR; }
13110#endif
13111#ifdef ETOOMANYREFS
13112 else if (e == ETOOMANYREFS) { return MA_ERROR; }
13113#endif
13114#ifdef ETIMEDOUT
13115 else if (e == ETIMEDOUT) { return MA_TIMEOUT; }
13116#endif
13117#ifdef ECONNREFUSED
13118 else if (e == ECONNREFUSED) { return MA_CONNECTION_REFUSED; }
13119#endif
13120#ifdef EHOSTDOWN
13121 else if (e == EHOSTDOWN) { return MA_NO_HOST; }
13122#endif
13123#ifdef EHOSTUNREACH
13124 else if (e == EHOSTUNREACH) { return MA_NO_HOST; }
13125#endif
13126#ifdef EALREADY
13127 else if (e == EALREADY) { return MA_IN_PROGRESS; }
13128#endif
13129#ifdef EINPROGRESS
13130 else if (e == EINPROGRESS) { return MA_IN_PROGRESS; }
13131#endif
13132#ifdef ESTALE
13133 else if (e == ESTALE) { return MA_INVALID_FILE; }
13134#endif
13135#ifdef EUCLEAN
13136 else if (e == EUCLEAN) { return MA_ERROR; }
13137#endif
13138#ifdef ENOTNAM
13139 else if (e == ENOTNAM) { return MA_ERROR; }
13140#endif
13141#ifdef ENAVAIL
13142 else if (e == ENAVAIL) { return MA_ERROR; }
13143#endif
13144#ifdef EISNAM
13145 else if (e == EISNAM) { return MA_ERROR; }
13146#endif
13147#ifdef EREMOTEIO
13148 else if (e == EREMOTEIO) { return MA_IO_ERROR; }
13149#endif
13150#ifdef EDQUOT
13151 else if (e == EDQUOT) { return MA_NO_SPACE; }
13152#endif
13153#ifdef ENOMEDIUM
13154 else if (e == ENOMEDIUM) { return MA_DOES_NOT_EXIST; }
13155#endif
13156#ifdef EMEDIUMTYPE
13157 else if (e == EMEDIUMTYPE) { return MA_ERROR; }
13158#endif
13159#ifdef ECANCELED
13160 else if (e == ECANCELED) { return MA_CANCELLED; }
13161#endif
13162#ifdef ENOKEY
13163 else if (e == ENOKEY) { return MA_ERROR; }
13164#endif
13165#ifdef EKEYEXPIRED
13166 else if (e == EKEYEXPIRED) { return MA_ERROR; }
13167#endif
13168#ifdef EKEYREVOKED
13169 else if (e == EKEYREVOKED) { return MA_ERROR; }
13170#endif
13171#ifdef EKEYREJECTED
13172 else if (e == EKEYREJECTED) { return MA_ERROR; }
13173#endif
13174#ifdef EOWNERDEAD
13175 else if (e == EOWNERDEAD) { return MA_ERROR; }
13176#endif
13177#ifdef ENOTRECOVERABLE
13178 else if (e == ENOTRECOVERABLE) { return MA_ERROR; }
13179#endif
13180#ifdef ERFKILL
13181 else if (e == ERFKILL) { return MA_ERROR; }
13182#endif
13183#ifdef EHWPOISON
13184 else if (e == EHWPOISON) { return MA_ERROR; }
13185#endif
13186 else {
13187 return MA_ERROR;
13188 }
13189}
13190
13191MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
13192{
13193#if defined(_MSC_VER) && _MSC_VER >= 1400
13194 errno_t err;
13195#endif
13196
13197 if (ppFile != NULL) {
13198 *ppFile = NULL; /* Safety. */
13199 }
13200
13201 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
13202 return MA_INVALID_ARGS;
13203 }
13204
13205#if defined(_MSC_VER) && _MSC_VER >= 1400
13206 err = fopen_s(ppFile, pFilePath, pOpenMode);
13207 if (err != 0) {
13208 return ma_result_from_errno(err);
13209 }
13210#else
13211#if defined(_WIN32) || defined(__APPLE__)
13212 *ppFile = fopen(pFilePath, pOpenMode);
13213#else
13214 #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
13215 *ppFile = fopen64(pFilePath, pOpenMode);
13216 #else
13217 *ppFile = fopen(pFilePath, pOpenMode);
13218 #endif
13219#endif
13220 if (*ppFile == NULL) {
13221 ma_result result = ma_result_from_errno(errno);
13222 if (result == MA_SUCCESS) {
13223 result = MA_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */
13224 }
13225
13226 return result;
13227 }
13228#endif
13229
13230 return MA_SUCCESS;
13231}
13232
13233
13234
13235/*
13236_wfopen() isn't always available in all compilation environments.
13237
13238 * Windows only.
13239 * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back).
13240 * MinGW-64 (both 32- and 64-bit) seems to support it.
13241 * MinGW wraps it in !defined(__STRICT_ANSI__).
13242 * OpenWatcom wraps it in !defined(_NO_EXT_KEYS).
13243
13244This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs()
13245fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support.
13246*/
13247#if defined(_WIN32)
13248 #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
13249 #define MA_HAS_WFOPEN
13250 #endif
13251#endif
13252
13253MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks)
13254{
13255 if (ppFile != NULL) {
13256 *ppFile = NULL; /* Safety. */
13257 }
13258
13259 if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
13260 return MA_INVALID_ARGS;
13261 }
13262
13263#if defined(MA_HAS_WFOPEN)
13264 {
13265 /* Use _wfopen() on Windows. */
13266 #if defined(_MSC_VER) && _MSC_VER >= 1400
13267 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
13268 if (err != 0) {
13269 return ma_result_from_errno(err);
13270 }
13271 #else
13272 *ppFile = _wfopen(pFilePath, pOpenMode);
13273 if (*ppFile == NULL) {
13274 return ma_result_from_errno(errno);
13275 }
13276 #endif
13277 (void)pAllocationCallbacks;
13278 }
13279#else
13280 /*
13281 Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can
13282 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
13283 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.
13284 */
13285 {
13286 mbstate_t mbs;
13287 size_t lenMB;
13288 const wchar_t* pFilePathTemp = pFilePath;
13289 char* pFilePathMB = NULL;
13290 char pOpenModeMB[32] = {0};
13291
13292 /* Get the length first. */
13293 MA_ZERO_OBJECT(&mbs);
13294 lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
13295 if (lenMB == (size_t)-1) {
13296 return ma_result_from_errno(errno);
13297 }
13298
13299 pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks);
13300 if (pFilePathMB == NULL) {
13301 return MA_OUT_OF_MEMORY;
13302 }
13303
13304 pFilePathTemp = pFilePath;
13305 MA_ZERO_OBJECT(&mbs);
13306 wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
13307
13308 /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */
13309 {
13310 size_t i = 0;
13311 for (;;) {
13312 if (pOpenMode[i] == 0) {
13313 pOpenModeMB[i] = '\0';
13314 break;
13315 }
13316
13317 pOpenModeMB[i] = (char)pOpenMode[i];
13318 i += 1;
13319 }
13320 }
13321
13322 *ppFile = fopen(pFilePathMB, pOpenModeMB);
13323
13324 ma_free(pFilePathMB, pAllocationCallbacks);
13325 }
13326
13327 if (*ppFile == NULL) {
13328 return MA_ERROR;
13329 }
13330#endif
13331
13332 return MA_SUCCESS;
13333}
13334
13335
13336
13337static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes)
13338{
13339#if MA_SIZE_MAX > 0xFFFFFFFF
13340 MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes);
13341#else
13342 while (sizeInBytes > 0) {
13343 ma_uint64 bytesToCopyNow = sizeInBytes;
13344 if (bytesToCopyNow > MA_SIZE_MAX) {
13345 bytesToCopyNow = MA_SIZE_MAX;
13346 }
13347
13348 MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */
13349
13350 sizeInBytes -= bytesToCopyNow;
13351 dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow);
13352 src = (const void*)((const ma_uint8*)src + bytesToCopyNow);
13353 }
13354#endif
13355}
13356
13357static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes)
13358{
13359#if MA_SIZE_MAX > 0xFFFFFFFF
13360 MA_ZERO_MEMORY(dst, (size_t)sizeInBytes);
13361#else
13362 while (sizeInBytes > 0) {
13363 ma_uint64 bytesToZeroNow = sizeInBytes;
13364 if (bytesToZeroNow > MA_SIZE_MAX) {
13365 bytesToZeroNow = MA_SIZE_MAX;
13366 }
13367
13368 MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */
13369
13370 sizeInBytes -= bytesToZeroNow;
13371 dst = (void*)((ma_uint8*)dst + bytesToZeroNow);
13372 }
13373#endif
13374}
13375
13376
13377/* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */
13378static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x)
13379{
13380 x--;
13381 x |= x >> 1;
13382 x |= x >> 2;
13383 x |= x >> 4;
13384 x |= x >> 8;
13385 x |= x >> 16;
13386 x++;
13387
13388 return x;
13389}
13390
13391static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x)
13392{
13393 return ma_next_power_of_2(x) >> 1;
13394}
13395
13396static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x)
13397{
13398 unsigned int prev = ma_prev_power_of_2(x);
13399 unsigned int next = ma_next_power_of_2(x);
13400 if ((next - x) > (x - prev)) {
13401 return prev;
13402 } else {
13403 return next;
13404 }
13405}
13406
13407static MA_INLINE unsigned int ma_count_set_bits(unsigned int x)
13408{
13409 unsigned int count = 0;
13410 while (x != 0) {
13411 if (x & 1) {
13412 count += 1;
13413 }
13414
13415 x = x >> 1;
13416 }
13417
13418 return count;
13419}
13420
13421
13422
13423/**************************************************************************************************************************************************************
13424
13425Allocation Callbacks
13426
13427**************************************************************************************************************************************************************/
13428static void* ma__malloc_default(size_t sz, void* pUserData)
13429{
13430 (void)pUserData;
13431 return MA_MALLOC(sz);
13432}
13433
13434static void* ma__realloc_default(void* p, size_t sz, void* pUserData)
13435{
13436 (void)pUserData;
13437 return MA_REALLOC(p, sz);
13438}
13439
13440static void ma__free_default(void* p, void* pUserData)
13441{
13442 (void)pUserData;
13443 MA_FREE(p);
13444}
13445
13446static ma_allocation_callbacks ma_allocation_callbacks_init_default(void)
13447{
13448 ma_allocation_callbacks callbacks;
13449 callbacks.pUserData = NULL;
13450 callbacks.onMalloc = ma__malloc_default;
13451 callbacks.onRealloc = ma__realloc_default;
13452 callbacks.onFree = ma__free_default;
13453
13454 return callbacks;
13455}
13456
13457static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc)
13458{
13459 if (pDst == NULL) {
13460 return MA_INVALID_ARGS;
13461 }
13462
13463 if (pSrc == NULL) {
13464 *pDst = ma_allocation_callbacks_init_default();
13465 } else {
13466 if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) {
13467 *pDst = ma_allocation_callbacks_init_default();
13468 } else {
13469 if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) {
13470 return MA_INVALID_ARGS; /* Invalid allocation callbacks. */
13471 } else {
13472 *pDst = *pSrc;
13473 }
13474 }
13475 }
13476
13477 return MA_SUCCESS;
13478}
13479
13480
13481
13482
13483/**************************************************************************************************************************************************************
13484
13485Logging
13486
13487**************************************************************************************************************************************************************/
13488#ifndef ma_va_copy
13489 #if !defined(_MSC_VER) || _MSC_VER >= 1800
13490 #if (defined(__GNUC__) && __GNUC__ < 3)
13491 #define ma_va_copy(dst, src) ((dst) = (src)) /* This is untested. Not sure if this is correct for old GCC. */
13492 #else
13493 #define ma_va_copy(dst, src) va_copy((dst), (src))
13494 #endif
13495 #else
13496 #define ma_va_copy(dst, src) ((dst) = (src))
13497 #endif
13498#endif
13499
13500MA_API const char* ma_log_level_to_string(ma_uint32 logLevel)
13501{
13502 switch (logLevel)
13503 {
13504 case MA_LOG_LEVEL_DEBUG: return "DEBUG";
13505 case MA_LOG_LEVEL_INFO: return "INFO";
13506 case MA_LOG_LEVEL_WARNING: return "WARNING";
13507 case MA_LOG_LEVEL_ERROR: return "ERROR";
13508 default: return "ERROR";
13509 }
13510}
13511
13512#if defined(MA_DEBUG_OUTPUT)
13513#if defined(MA_ANDROID)
13514 #include <android/log.h>
13515#endif
13516
13517/* Customize this to use a specific tag in __android_log_print() for debug output messages. */
13518#ifndef MA_ANDROID_LOG_TAG
13519#define MA_ANDROID_LOG_TAG "miniaudio"
13520#endif
13521
13522void ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage)
13523{
13524 (void)pUserData;
13525
13526 /* Special handling for some platforms. */
13527 #if defined(MA_ANDROID)
13528 {
13529 /* Android. */
13530 __android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, "%s: %s", ma_log_level_to_string(level), pMessage);
13531 }
13532 #else
13533 {
13534 /* Everything else. */
13535 printf("%s: %s", ma_log_level_to_string(level), pMessage);
13536 }
13537 #endif
13538}
13539#endif
13540
13542{
13543 ma_log_callback callback;
13544
13545 MA_ZERO_OBJECT(&callback);
13546 callback.onLog = onLog;
13547 callback.pUserData = pUserData;
13548
13549 return callback;
13550}
13551
13552
13553MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog)
13554{
13555 if (pLog == NULL) {
13556 return MA_INVALID_ARGS;
13557 }
13558
13559 MA_ZERO_OBJECT(pLog);
13560 ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks);
13561
13562 /* We need a mutex for thread safety. */
13563 #ifndef MA_NO_THREADING
13564 {
13565 ma_result result = ma_mutex_init(&pLog->lock);
13566 if (result != MA_SUCCESS) {
13567 return result;
13568 }
13569 }
13570 #endif
13571
13572 /* If we're using debug output, enable it. */
13573 #if defined(MA_DEBUG_OUTPUT)
13574 {
13575 ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL)); /* Doesn't really matter if this fails. */
13576 }
13577 #endif
13578
13579 return MA_SUCCESS;
13580}
13581
13582MA_API void ma_log_uninit(ma_log* pLog)
13583{
13584 if (pLog == NULL) {
13585 return;
13586 }
13587
13588#ifndef MA_NO_THREADING
13589 ma_mutex_uninit(&pLog->lock);
13590#endif
13591}
13592
13593static void ma_log_lock(ma_log* pLog)
13594{
13595#ifndef MA_NO_THREADING
13596 ma_mutex_lock(&pLog->lock);
13597#else
13598 (void)pLog;
13599#endif
13600}
13601
13602static void ma_log_unlock(ma_log* pLog)
13603{
13604#ifndef MA_NO_THREADING
13605 ma_mutex_unlock(&pLog->lock);
13606#else
13607 (void)pLog;
13608#endif
13609}
13610
13612{
13613 ma_result result = MA_SUCCESS;
13614
13615 if (pLog == NULL || callback.onLog == NULL) {
13616 return MA_INVALID_ARGS;
13617 }
13618
13619 ma_log_lock(pLog);
13620 {
13621 if (pLog->callbackCount == ma_countof(pLog->callbacks)) {
13622 result = MA_OUT_OF_MEMORY; /* Reached the maximum allowed log callbacks. */
13623 } else {
13624 pLog->callbacks[pLog->callbackCount] = callback;
13625 pLog->callbackCount += 1;
13626 }
13627 }
13628 ma_log_unlock(pLog);
13629
13630 return result;
13631}
13632
13634{
13635 if (pLog == NULL) {
13636 return MA_INVALID_ARGS;
13637 }
13638
13639 ma_log_lock(pLog);
13640 {
13641 ma_uint32 iLog;
13642 for (iLog = 0; iLog < pLog->callbackCount; ) {
13643 if (pLog->callbacks[iLog].onLog == callback.onLog) {
13644 /* Found. Move everything down a slot. */
13645 ma_uint32 jLog;
13646 for (jLog = iLog; jLog < pLog->callbackCount-1; jLog += 1) {
13647 pLog->callbacks[jLog] = pLog->callbacks[jLog + 1];
13648 }
13649
13650 pLog->callbackCount -= 1;
13651 } else {
13652 /* Not found. */
13653 iLog += 1;
13654 }
13655 }
13656 }
13657 ma_log_unlock(pLog);
13658
13659 return MA_SUCCESS;
13660}
13661
13662MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage)
13663{
13664 if (pLog == NULL || pMessage == NULL) {
13665 return MA_INVALID_ARGS;
13666 }
13667
13668 ma_log_lock(pLog);
13669 {
13670 ma_uint32 iLog;
13671 for (iLog = 0; iLog < pLog->callbackCount; iLog += 1) {
13672 if (pLog->callbacks[iLog].onLog) {
13673 pLog->callbacks[iLog].onLog(pLog->callbacks[iLog].pUserData, level, pMessage);
13674 }
13675 }
13676 }
13677 ma_log_unlock(pLog);
13678
13679 return MA_SUCCESS;
13680}
13681
13682
13683/*
13684We need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a
13685logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf().
13686*/
13687#if defined(_MSC_VER) && _MSC_VER < 1900
13688static int ma_vscprintf(const ma_allocation_callbacks* pAllocationCallbacks, const char* format, va_list args)
13689{
13690#if _MSC_VER > 1200
13691 return _vscprintf(format, args);
13692#else
13693 int result;
13694 char* pTempBuffer = NULL;
13695 size_t tempBufferCap = 1024;
13696
13697 if (format == NULL) {
13698 errno = EINVAL;
13699 return -1;
13700 }
13701
13702 for (;;) {
13703 char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, pAllocationCallbacks);
13704 if (pNewTempBuffer == NULL) {
13705 ma_free(pTempBuffer, pAllocationCallbacks);
13706 errno = ENOMEM;
13707 return -1; /* Out of memory. */
13708 }
13709
13710 pTempBuffer = pNewTempBuffer;
13711
13712 result = _vsnprintf(pTempBuffer, tempBufferCap, format, args);
13713 ma_free(pTempBuffer, NULL);
13714
13715 if (result != -1) {
13716 break; /* Got it. */
13717 }
13718
13719 /* 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. */
13720 tempBufferCap *= 2;
13721 }
13722
13723 return result;
13724#endif
13725}
13726#endif
13727
13728MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args)
13729{
13730 if (pLog == NULL || pFormat == NULL) {
13731 return MA_INVALID_ARGS;
13732 }
13733
13734 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) || (defined(__cplusplus) && __cplusplus >= 201103L)
13735 {
13736 ma_result result;
13737 int length;
13738 char pFormattedMessageStack[1024];
13739 char* pFormattedMessageHeap = NULL;
13740 va_list args2;
13741
13742 /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */
13743 ma_va_copy(args2, args);
13744 {
13745 length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args2);
13746 }
13747 va_end(args2);
13748
13749 if (length < 0) {
13750 return MA_INVALID_OPERATION; /* An error occurred when trying to convert the buffer. */
13751 }
13752
13753 if ((size_t)length < sizeof(pFormattedMessageStack)) {
13754 /* The string was written to the stack. */
13755 result = ma_log_post(pLog, level, pFormattedMessageStack);
13756 } else {
13757 /* The stack buffer was too small, try the heap. */
13758 pFormattedMessageHeap = (char*)ma_malloc(length + 1, &pLog->allocationCallbacks);
13759 if (pFormattedMessageHeap == NULL) {
13760 return MA_OUT_OF_MEMORY;
13761 }
13762
13763 length = vsnprintf(pFormattedMessageHeap, length + 1, pFormat, args);
13764 if (length < 0) {
13765 ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks);
13766 return MA_INVALID_OPERATION;
13767 }
13768
13769 result = ma_log_post(pLog, level, pFormattedMessageHeap);
13770 ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks);
13771 }
13772
13773 return result;
13774 }
13775 #else
13776 {
13777 /*
13778 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
13779 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
13780 a fixed sized stack allocated buffer.
13781 */
13782 #if defined(_MSC_VER) && _MSC_VER >= 1200 /* 1200 = VC6 */
13783 {
13784 ma_result result;
13785 int formattedLen;
13786 char* pFormattedMessage = NULL;
13787 va_list args2;
13788
13789 ma_va_copy(args2, args);
13790 {
13791 formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2);
13792 }
13793 va_end(args2);
13794
13795 if (formattedLen <= 0) {
13796 return MA_INVALID_OPERATION;
13797 }
13798
13799 pFormattedMessage = (char*)ma_malloc(formattedLen + 1, &pLog->allocationCallbacks);
13800 if (pFormattedMessage == NULL) {
13801 return MA_OUT_OF_MEMORY;
13802 }
13803
13804 /* We'll get errors on newer versions of Visual Studio if we try to use vsprintf(). */
13805 #if _MSC_VER >= 1400 /* 1400 = Visual Studio 2005 */
13806 {
13807 vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args);
13808 }
13809 #else
13810 {
13811 vsprintf(pFormattedMessage, pFormat, args);
13812 }
13813 #endif
13814
13815 result = ma_log_post(pLog, level, pFormattedMessage);
13816 ma_free(pFormattedMessage, &pLog->allocationCallbacks);
13817
13818 return result;
13819 }
13820 #else
13821 {
13822 /* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */
13823 (void)level;
13824 (void)args;
13825
13826 return MA_INVALID_OPERATION;
13827 }
13828 #endif
13829 }
13830 #endif
13831}
13832
13833MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...)
13834{
13835 ma_result result;
13836 va_list args;
13837
13838 if (pLog == NULL || pFormat == NULL) {
13839 return MA_INVALID_ARGS;
13840 }
13841
13842 va_start(args, pFormat);
13843 {
13844 result = ma_log_postv(pLog, level, pFormat, args);
13845 }
13846 va_end(args);
13847
13848 return result;
13849}
13850
13851
13852
13853static MA_INLINE ma_uint8 ma_clip_u8(ma_int32 x)
13854{
13855 return (ma_uint8)(ma_clamp(x, -128, 127) + 128);
13856}
13857
13858static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x)
13859{
13860 return (ma_int16)ma_clamp(x, -32768, 32767);
13861}
13862
13863static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x)
13864{
13865 return (ma_int64)ma_clamp(x, -8388608, 8388607);
13866}
13867
13868static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x)
13869{
13870 /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */
13871 ma_int64 clipMin;
13872 ma_int64 clipMax;
13873 clipMin = -((ma_int64)2147483647 + 1);
13874 clipMax = (ma_int64)2147483647;
13875
13876 return (ma_int32)ma_clamp(x, clipMin, clipMax);
13877}
13878
13879static MA_INLINE float ma_clip_f32(float x)
13880{
13881 if (x < -1) return -1;
13882 if (x > +1) return +1;
13883 return x;
13884}
13885
13886
13887static MA_INLINE float ma_mix_f32(float x, float y, float a)
13888{
13889 return x*(1-a) + y*a;
13890}
13891static MA_INLINE float ma_mix_f32_fast(float x, float y, float a)
13892{
13893 float r0 = (y - x);
13894 float r1 = r0*a;
13895 return x + r1;
13896 /*return x + (y - x)*a;*/
13897}
13898
13899#if defined(MA_SUPPORT_SSE2)
13900static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a)
13901{
13902 return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a));
13903}
13904#endif
13905#if defined(MA_SUPPORT_AVX2)
13906static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a)
13907{
13908 return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a));
13909}
13910#endif
13911#if defined(MA_SUPPORT_NEON)
13912static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a)
13913{
13914 return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a));
13915}
13916#endif
13917
13918
13919static MA_INLINE double ma_mix_f64(double x, double y, double a)
13920{
13921 return x*(1-a) + y*a;
13922}
13923static MA_INLINE double ma_mix_f64_fast(double x, double y, double a)
13924{
13925 return x + (y - x)*a;
13926}
13927
13928static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi)
13929{
13930 return lo + x*(hi-lo);
13931}
13932
13933
13934/*
13935Greatest common factor using Euclid's algorithm iteratively.
13936*/
13937static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b)
13938{
13939 for (;;) {
13940 if (b == 0) {
13941 break;
13942 } else {
13943 ma_uint32 t = a;
13944 a = b;
13945 b = t % a;
13946 }
13947 }
13948
13949 return a;
13950}
13951
13952
13953static ma_uint32 ma_ffs_32(ma_uint32 x)
13954{
13955 ma_uint32 i;
13956
13957 /* Just a naive implementation just to get things working for now. Will optimize this later. */
13958 for (i = 0; i < 32; i += 1) {
13959 if ((x & (1U << i)) != 0) {
13960 return i;
13961 }
13962 }
13963
13964 return i;
13965}
13966
13967static MA_INLINE ma_int16 ma_float_to_fixed_16(float x)
13968{
13969 return (ma_int16)(x * (1 << 8));
13970}
13971
13972
13973
13974/*
13975Random Number Generation
13976
13977miniaudio uses the LCG random number generation algorithm. This is good enough for audio.
13978
13979Note that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across
13980multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for
13981miniaudio's purposes.
13982*/
13983#ifndef MA_DEFAULT_LCG_SEED
13984#define MA_DEFAULT_LCG_SEED 4321
13985#endif
13986
13987#define MA_LCG_M 2147483647
13988#define MA_LCG_A 48271
13989#define MA_LCG_C 0
13990
13991static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_lcg_seed() to use an explicit seed. */
13992
13993static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed)
13994{
13995 MA_ASSERT(pLCG != NULL);
13996 pLCG->state = seed;
13997}
13998
13999static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG)
14000{
14001 pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M;
14002 return pLCG->state;
14003}
14004
14005static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG)
14006{
14007 return (ma_uint32)ma_lcg_rand_s32(pLCG);
14008}
14009
14010static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG)
14011{
14012 return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF);
14013}
14014
14015static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG)
14016{
14017 return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF;
14018}
14019
14020static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG)
14021{
14022 return (float)ma_lcg_rand_f64(pLCG);
14023}
14024
14025static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi)
14026{
14027 return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi);
14028}
14029
14030static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi)
14031{
14032 if (lo == hi) {
14033 return lo;
14034 }
14035
14036 return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1);
14037}
14038
14039
14040#if 0 /* Currently unused. */
14041static MA_INLINE void ma_seed(ma_int32 seed)
14042{
14043 ma_lcg_seed(&g_maLCG, seed);
14044}
14045
14046static MA_INLINE ma_int32 ma_rand_s32(void)
14047{
14048 return ma_lcg_rand_s32(&g_maLCG);
14049}
14050
14051static MA_INLINE ma_uint32 ma_rand_u32(void)
14052{
14053 return ma_lcg_rand_u32(&g_maLCG);
14054}
14055
14056static MA_INLINE double ma_rand_f64(void)
14057{
14058 return ma_lcg_rand_f64(&g_maLCG);
14059}
14060
14061static MA_INLINE float ma_rand_f32(void)
14062{
14063 return ma_lcg_rand_f32(&g_maLCG);
14064}
14065#endif
14066
14067static MA_INLINE float ma_rand_range_f32(float lo, float hi)
14068{
14069 return ma_lcg_rand_range_f32(&g_maLCG, lo, hi);
14070}
14071
14072static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi)
14073{
14074 return ma_lcg_rand_range_s32(&g_maLCG, lo, hi);
14075}
14076
14077
14078static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax)
14079{
14080 return ma_rand_range_f32(ditherMin, ditherMax);
14081}
14082
14083static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax)
14084{
14085 float a = ma_rand_range_f32(ditherMin, 0);
14086 float b = ma_rand_range_f32(0, ditherMax);
14087 return a + b;
14088}
14089
14090static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax)
14091{
14092 if (ditherMode == ma_dither_mode_rectangle) {
14093 return ma_dither_f32_rectangle(ditherMin, ditherMax);
14094 }
14095 if (ditherMode == ma_dither_mode_triangle) {
14096 return ma_dither_f32_triangle(ditherMin, ditherMax);
14097 }
14098
14099 return 0;
14100}
14101
14102static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax)
14103{
14104 if (ditherMode == ma_dither_mode_rectangle) {
14105 ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax);
14106 return a;
14107 }
14108 if (ditherMode == ma_dither_mode_triangle) {
14109 ma_int32 a = ma_rand_range_s32(ditherMin, 0);
14110 ma_int32 b = ma_rand_range_s32(0, ditherMax);
14111 return a + b;
14112 }
14113
14114 return 0;
14115}
14116
14117
14118/**************************************************************************************************************************************************************
14119
14120Atomics
14121
14122**************************************************************************************************************************************************************/
14123/* c89atomic.h begin */
14124#ifndef ma_atomic_h
14125#define ma_atomic_h
14126#if defined(__cplusplus)
14127extern "C" {
14128#endif
14129#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
14130 #pragma GCC diagnostic push
14131 #pragma GCC diagnostic ignored "-Wlong-long"
14132 #if defined(__clang__)
14133 #pragma GCC diagnostic ignored "-Wc++11-long-long"
14134 #endif
14135#endif
14136typedef int ma_atomic_memory_order;
14137#if !defined(MA_ATOMIC_MODERN_MSVC) && \
14138 !defined(MA_ATOMIC_LEGACY_MSVC) && \
14139 !defined(MA_ATOMIC_LEGACY_MSVC_ASM) && \
14140 !defined(MA_ATOMIC_MODERN_GCC) && \
14141 !defined(MA_ATOMIC_LEGACY_GCC) && \
14142 !defined(MA_ATOMIC_LEGACY_GCC_ASM)
14143 #if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__) || defined(__BORLANDC__)
14144 #if (defined(_MSC_VER) && _MSC_VER > 1600)
14145 #define MA_ATOMIC_MODERN_MSVC
14146 #else
14147 #if defined(MA_X64)
14148 #define MA_ATOMIC_LEGACY_MSVC
14149 #else
14150 #define MA_ATOMIC_LEGACY_MSVC_ASM
14151 #endif
14152 #endif
14153 #elif (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) || defined(__clang__)
14154 #define MA_ATOMIC_MODERN_GCC
14155 #else
14156 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1))
14157 #define MA_ATOMIC_LEGACY_GCC
14158 #else
14159 #define MA_ATOMIC_LEGACY_GCC_ASM
14160 #endif
14161 #endif
14162#endif
14163#if defined(MA_ATOMIC_MODERN_MSVC) || defined(MA_ATOMIC_LEGACY_MSVC)
14164 #include <intrin.h>
14165 #define ma_atomic_memory_order_relaxed 1
14166 #define ma_atomic_memory_order_consume 2
14167 #define ma_atomic_memory_order_acquire 3
14168 #define ma_atomic_memory_order_release 4
14169 #define ma_atomic_memory_order_acq_rel 5
14170 #define ma_atomic_memory_order_seq_cst 6
14171 #define MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, ma_atomicType, msvcType) \
14172 ma_atomicType result; \
14173 switch (order) \
14174 { \
14175 case ma_atomic_memory_order_relaxed: \
14176 { \
14177 result = (ma_atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \
14178 } break; \
14179 case ma_atomic_memory_order_consume: \
14180 case ma_atomic_memory_order_acquire: \
14181 { \
14182 result = (ma_atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \
14183 } break; \
14184 case ma_atomic_memory_order_release: \
14185 { \
14186 result = (ma_atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \
14187 } break; \
14188 case ma_atomic_memory_order_acq_rel: \
14189 case ma_atomic_memory_order_seq_cst: \
14190 default: \
14191 { \
14192 result = (ma_atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \
14193 } break; \
14194 } \
14195 return result;
14196 typedef ma_uint32 ma_atomic_flag;
14197 static MA_INLINE ma_atomic_flag ma_atomic_flag_test_and_set_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order)
14198 {
14199 #if defined(MA_ARM)
14200 {
14201 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, 1, order, _InterlockedExchange, ma_atomic_flag, long);
14202 }
14203 #else
14204 {
14205 (void)order;
14206 return (ma_atomic_flag)_InterlockedExchange((volatile long*)dst, (long)1);
14207 }
14208 #endif
14209 }
14210 static MA_INLINE void ma_atomic_flag_clear_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order)
14211 {
14212 #if defined(MA_ARM)
14213 {
14214 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, 0, order, _InterlockedExchange, ma_atomic_flag, long);
14215 }
14216 #else
14217 {
14218 (void)order;
14219 _InterlockedExchange((volatile long*)dst, (long)0);
14220 }
14221 #endif
14222 }
14223 static MA_INLINE ma_atomic_flag ma_atomic_flag_load_explicit(volatile const ma_atomic_flag* dst, ma_atomic_memory_order order)
14224 {
14225 (void)order;
14226 return (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, 0, 0);
14227 }
14228#endif
14229#if defined(MA_ATOMIC_LEGACY_MSVC_ASM)
14230 #define ma_atomic_memory_order_relaxed 1
14231 #define ma_atomic_memory_order_consume 2
14232 #define ma_atomic_memory_order_acquire 3
14233 #define ma_atomic_memory_order_release 4
14234 #define ma_atomic_memory_order_acq_rel 5
14235 #define ma_atomic_memory_order_seq_cst 6
14236 typedef ma_uint32 ma_atomic_flag;
14237 static MA_INLINE ma_atomic_flag ma_atomic_flag_test_and_set_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order)
14238 {
14239 ma_atomic_flag result = 0;
14240 (void)order;
14241 __asm {
14242 mov ecx, dst
14243 mov eax, 1
14244 xchg [ecx], eax
14245 mov result, eax
14246 }
14247 return result;
14248 }
14249 static MA_INLINE void ma_atomic_flag_clear_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order)
14250 {
14251 if (order == ma_atomic_memory_order_relaxed) {
14252 __asm {
14253 mov esi, dst
14254 mov dword ptr [esi], 0
14255 }
14256 } else {
14257 __asm {
14258 mov esi, dst
14259 mov eax, 0
14260 xchg [esi], eax
14261 }
14262 }
14263 }
14264 static MA_INLINE ma_atomic_flag ma_atomic_flag_load_explicit(volatile const ma_atomic_flag* dst, ma_atomic_memory_order order)
14265 {
14266 ma_atomic_flag result = 0;
14267 if (order == ma_atomic_memory_order_relaxed) {
14268 __asm {
14269 mov esi, dst
14270 mov eax, [esi]
14271 mov result, eax
14272 }
14273 } else if (order <= ma_atomic_memory_order_release) {
14274 __asm {
14275 mov esi, dst
14276 mov eax, [esi]
14277 lock add dword ptr [esp], 0
14278 mov result, eax
14279 }
14280 } else {
14281 __asm {
14282 lock add dword ptr [esp], 0
14283 mov esi, dst
14284 mov eax, [esi]
14285 mov result, eax
14286 lock add dword ptr [esp], 0
14287 }
14288 }
14289 return result;
14290 }
14291#endif
14292#if defined(MA_ATOMIC_MODERN_GCC)
14293 #define ma_atomic_memory_order_relaxed __ATOMIC_RELAXED
14294 #define ma_atomic_memory_order_consume __ATOMIC_CONSUME
14295 #define ma_atomic_memory_order_acquire __ATOMIC_ACQUIRE
14296 #define ma_atomic_memory_order_release __ATOMIC_RELEASE
14297 #define ma_atomic_memory_order_acq_rel __ATOMIC_ACQ_REL
14298 #define ma_atomic_memory_order_seq_cst __ATOMIC_SEQ_CST
14299 typedef ma_uint32 ma_atomic_flag;
14300 #define ma_atomic_flag_test_and_set_explicit(dst, order) __atomic_exchange_n(dst, 1, order)
14301 #define ma_atomic_flag_clear_explicit(dst, order) __atomic_store_n(dst, 0, order)
14302 #define ma_atomic_flag_load_explicit(dst, order) __atomic_load_n(dst, order)
14303#endif
14304#if defined(MA_ATOMIC_LEGACY_GCC)
14305 #define ma_atomic_memory_order_relaxed 1
14306 #define ma_atomic_memory_order_consume 2
14307 #define ma_atomic_memory_order_acquire 3
14308 #define ma_atomic_memory_order_release 4
14309 #define ma_atomic_memory_order_acq_rel 5
14310 #define ma_atomic_memory_order_seq_cst 6
14311 typedef ma_uint32 ma_atomic_flag;
14312 static MA_INLINE ma_atomic_flag ma_atomic_flag_test_and_set_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order)
14313 {
14314 if (order > ma_atomic_memory_order_acquire) {
14315 __sync_synchronize();
14316 }
14317 return __sync_lock_test_and_set(dst, 1);
14318 }
14319 static MA_INLINE void ma_atomic_flag_clear_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order)
14320 {
14321 if (order > ma_atomic_memory_order_release) {
14322 __sync_synchronize();
14323 }
14324 __sync_lock_release(dst);
14325 }
14326 static MA_INLINE ma_atomic_flag ma_atomic_flag_load_explicit(volatile const ma_atomic_flag* dst, ma_atomic_memory_order order)
14327 {
14328 (void)order;
14329 return __sync_val_compare_and_swap((ma_atomic_flag*)dst, 0, 0);
14330 }
14331#endif
14332#if defined(MA_ATOMIC_LEGACY_GCC_ASM)
14333 #define ma_atomic_memory_order_relaxed 1
14334 #define ma_atomic_memory_order_consume 2
14335 #define ma_atomic_memory_order_acquire 3
14336 #define ma_atomic_memory_order_release 4
14337 #define ma_atomic_memory_order_acq_rel 5
14338 #define ma_atomic_memory_order_seq_cst 6
14339 #if defined(MA_X86)
14340 #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory")
14341 #elif defined(MA_X64)
14342 #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory")
14343 #else
14344 #error Unsupported architecture.
14345 #endif
14346 #define MA_ATOMIC_XCHG_GCC_X86(instructionSizeSuffix, result, dst, src) \
14347 __asm__ __volatile__( \
14348 "xchg"instructionSizeSuffix" %0, %1" \
14349 : "=r"(result), \
14350 "=m"(*dst) \
14351 : "0"(src), \
14352 "m"(*dst) \
14353 : "memory" \
14354 )
14355 #define MA_ATOMIC_LOAD_RELAXED_GCC_X86(instructionSizeSuffix, result, dst) \
14356 __asm__ __volatile__( \
14357 "mov"instructionSizeSuffix" %1, %0" \
14358 : "=r"(result) \
14359 : "m"(*dst) \
14360 )
14361 #define MA_ATOMIC_LOAD_RELEASE_GCC_X86(instructionSizeSuffix, result, dst) \
14362 ma_atomic_thread_fence(ma_atomic_memory_order_release); \
14363 __asm__ __volatile__( \
14364 "mov"instructionSizeSuffix" %1, %0" \
14365 : "=r"(result) \
14366 : "m"(*dst) \
14367 : "memory" \
14368 )
14369 #define MA_ATOMIC_LOAD_SEQ_CST_GCC_X86(instructionSizeSuffix, result, dst) \
14370 ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst); \
14371 __asm__ __volatile__( \
14372 "mov"instructionSizeSuffix" %1, %0" \
14373 : "=r"(result) \
14374 : "m"(*dst) \
14375 : "memory" \
14376 ); \
14377 ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst)
14378 typedef ma_uint32 ma_atomic_flag;
14379 static MA_INLINE ma_atomic_flag ma_atomic_flag_test_and_set_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order)
14380 {
14381 ma_atomic_flag result;
14382 #if defined(MA_X86) || defined(MA_X64)
14383 {
14384 (void)order;
14385 MA_ATOMIC_XCHG_GCC_X86("l", result, dst, 1);
14386 }
14387 #else
14388 {
14389 #error Unsupported architecture.
14390 }
14391 #endif
14392 return result;
14393 }
14394 static MA_INLINE void ma_atomic_flag_clear_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order)
14395 {
14396 #if defined(MA_X86) || defined(MA_X64)
14397 {
14398 if (order == ma_atomic_memory_order_relaxed) {
14399 __asm__ __volatile__(
14400 "movl $0, %0"
14401 : "=m"(*dst)
14402 );
14403 } else if (order == ma_atomic_memory_order_release) {
14404 __asm__ __volatile__(
14405 "movl $0, %0"
14406 : "=m"(*dst)
14407 :
14408 : "memory"
14409 );
14410 } else {
14411 ma_atomic_flag tmp = 0;
14412 __asm__ __volatile__(
14413 "xchgl %0, %1"
14414 : "=r"(tmp),
14415 "=m"(*dst)
14416 : "0"(tmp),
14417 "m"(*dst)
14418 : "memory"
14419 );
14420 }
14421 }
14422 #else
14423 {
14424 #error Unsupported architecture.
14425 }
14426 #endif
14427 }
14428 static MA_INLINE ma_atomic_flag ma_atomic_flag_load_explicit(volatile const ma_atomic_flag* dst, ma_atomic_memory_order order)
14429 {
14430 #if defined(MA_X86) || defined(MA_X64)
14431 {
14432 ma_atomic_flag result;
14433 if (order == ma_atomic_memory_order_relaxed) {
14434 MA_ATOMIC_LOAD_RELAXED_GCC_X86("l", result, dst);
14435 } else if (order <= ma_atomic_memory_order_release) {
14436 MA_ATOMIC_LOAD_RELEASE_GCC_X86("l", result, dst);
14437 } else {
14438 MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("l", result, dst);
14439 }
14440 return result;
14441 }
14442 #else
14443 {
14444 #error Unsupported architecture.
14445 }
14446 #endif
14447 }
14448#endif
14449#define ma_atomic_flag_test_and_set(dst) ma_atomic_flag_test_and_set_explicit(dst, ma_atomic_memory_order_acquire)
14450#define ma_atomic_flag_clear(dst) ma_atomic_flag_clear_explicit(dst, ma_atomic_memory_order_release)
14451typedef ma_atomic_flag ma_atomic_spinlock;
14452static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock)
14453{
14454 for (;;) {
14455 if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) {
14456 break;
14457 }
14458 while (ma_atomic_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) {
14459 }
14460 }
14461}
14462static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock)
14463{
14464 ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release);
14465}
14466ma_atomic_spinlock ma_atomic_global_lock;
14467#if defined(MA_ATOMIC_MODERN_MSVC) || defined(MA_ATOMIC_LEGACY_MSVC) || defined(MA_ATOMIC_LEGACY_MSVC_ASM) || defined(MA_ATOMIC_LEGACY_GCC) || defined(MA_ATOMIC_LEGACY_GCC_ASM)
14468 #if defined(MA_X64) || (defined(MA_X86) && ((defined(__GNUC__) && defined(__i486__)) || (defined(_M_IX86) && _M_IX86 >= 400)))
14469 #if defined(MA_ATOMIC_LEGACY_MSVC) && defined(MA_X64)
14470 #else
14471 #define MA_ATOMIC_IS_LOCK_FREE_8 1
14472 #define MA_ATOMIC_IS_LOCK_FREE_16 1
14473 #endif
14474 #define MA_ATOMIC_IS_LOCK_FREE_32 1
14475 #if defined(MA_X64) || (defined(MA_X86) && ((defined(__GNUC__) && defined(__i586__)) || (defined(_M_IX86) && _M_IX86 >= 500)))
14476 #define MA_ATOMIC_IS_LOCK_FREE_64 1
14477 #else
14478 #endif
14479 #else
14480 #endif
14481 #if defined(MA_ARM32) || defined(MA_ARM64)
14482 #define MA_ATOMIC_IS_LOCK_FREE_8 1
14483 #define MA_ATOMIC_IS_LOCK_FREE_16 1
14484 #define MA_ATOMIC_IS_LOCK_FREE_32 1
14485 #if defined(MA_ARM64) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__)
14486 #define MA_ATOMIC_IS_LOCK_FREE_64 1
14487 #endif
14488 #endif
14489 #if defined(MA_ATOMIC_PPC32) || defined(MA_ATOMIC_PPC64)
14490 #if (defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7))) && !defined(__clang__)
14491 #else
14492 #define MA_ATOMIC_IS_LOCK_FREE_8 1
14493 #define MA_ATOMIC_IS_LOCK_FREE_16 1
14494 #endif
14495 #define MA_ATOMIC_IS_LOCK_FREE_32 1
14496 #if defined(MA_ATOMIC_PPC64)
14497 #define MA_ATOMIC_IS_LOCK_FREE_64 1
14498 #endif
14499 #endif
14500 static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr)
14501 {
14502 (void)ptr;
14503 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
14504 return 1;
14505 #else
14506 return 0;
14507 #endif
14508 }
14509 static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr)
14510 {
14511 (void)ptr;
14512 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
14513 return 1;
14514 #else
14515 return 0;
14516 #endif
14517 }
14518 static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr)
14519 {
14520 (void)ptr;
14521 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
14522 return 1;
14523 #else
14524 return 0;
14525 #endif
14526 }
14527 static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr)
14528 {
14529 (void)ptr;
14530 #if defined(MA_ATOMIC_IS_LOCK_FREE_64)
14531 return 1;
14532 #else
14533 return 0;
14534 #endif
14535 }
14536#endif
14537#define MA_ATOMIC_COMPARE_AND_SWAP_LOCK(sizeInBits, dst, expected, replacement) \
14538 ma_uint##sizeInBits result; \
14539 ma_atomic_spinlock_lock(&ma_atomic_global_lock); \
14540 { \
14541 result = *dst; \
14542 if (result == expected) { \
14543 *dst = replacement; \
14544 } \
14545 } \
14546 ma_atomic_spinlock_unlock(&ma_atomic_global_lock); \
14547 return result
14548#define MA_ATOMIC_LOAD_EXPLICIT_LOCK(sizeInBits, ptr, order) \
14549 ma_uint##sizeInBits result; \
14550 ma_atomic_spinlock_lock(&ma_atomic_global_lock); \
14551 { \
14552 result = *ptr; \
14553 (void)order; \
14554 } \
14555 ma_atomic_spinlock_unlock(&ma_atomic_global_lock); \
14556 return result
14557#define MA_ATOMIC_STORE_EXPLICIT_LOCK(sizeInBits, dst, src, order) \
14558 ma_atomic_spinlock_lock(&ma_atomic_global_lock); \
14559 { \
14560 *dst = src; \
14561 (void)order; \
14562 } \
14563 ma_atomic_spinlock_unlock(&ma_atomic_global_lock)
14564#define MA_ATOMIC_STORE_EXPLICIT_CAS(sizeInBits, dst, src, order) \
14565 ma_uint##sizeInBits oldValue; \
14566 do { \
14567 oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \
14568 } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, src) != oldValue); \
14569 (void)order
14570#define MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(sizeInBits, dst, src, order) \
14571 ma_uint##sizeInBits result; \
14572 ma_atomic_spinlock_lock(&ma_atomic_global_lock); \
14573 { \
14574 result = *dst; \
14575 *dst = src; \
14576 (void)order; \
14577 } \
14578 ma_atomic_spinlock_unlock(&ma_atomic_global_lock); \
14579 return result
14580#define MA_ATOMIC_EXCHANGE_EXPLICIT_CAS(sizeInBits, dst, src, order) \
14581 ma_uint##sizeInBits oldValue; \
14582 do { \
14583 oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \
14584 } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, src) != oldValue); \
14585 (void)order; \
14586 return oldValue
14587#define MA_ATOMIC_FETCH_ADD_LOCK(sizeInBits, dst, src, order) \
14588 ma_uint##sizeInBits result; \
14589 ma_atomic_spinlock_lock(&ma_atomic_global_lock); \
14590 { \
14591 result = *dst; \
14592 *dst += src; \
14593 (void)order; \
14594 } \
14595 ma_atomic_spinlock_unlock(&ma_atomic_global_lock); \
14596 return result
14597#define MA_ATOMIC_FETCH_ADD_CAS(sizeInBits, dst, src, order) \
14598 ma_uint##sizeInBits oldValue; \
14599 ma_uint##sizeInBits newValue; \
14600 do { \
14601 oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \
14602 newValue = oldValue + src; \
14603 } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, newValue) != oldValue); \
14604 (void)order; \
14605 return oldValue
14606#define MA_ATOMIC_FETCH_AND_CAS(sizeInBits, dst, src, order) \
14607 ma_uint##sizeInBits oldValue; \
14608 ma_uint##sizeInBits newValue; \
14609 do { \
14610 oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \
14611 newValue = (ma_uint##sizeInBits)(oldValue & src); \
14612 } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, newValue) != oldValue); \
14613 (void)order; \
14614 return oldValue
14615#define MA_ATOMIC_FETCH_OR_CAS(sizeInBits, dst, src, order) \
14616 ma_uint##sizeInBits oldValue; \
14617 ma_uint##sizeInBits newValue; \
14618 do { \
14619 oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \
14620 newValue = (ma_uint##sizeInBits)(oldValue | src); \
14621 } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, newValue) != oldValue); \
14622 (void)order; \
14623 return oldValue
14624#define MA_ATOMIC_FETCH_XOR_CAS(sizeInBits, dst, src, order) \
14625 ma_uint##sizeInBits oldValue; \
14626 ma_uint##sizeInBits newValue; \
14627 do { \
14628 oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \
14629 newValue = (ma_uint##sizeInBits)(oldValue ^ src); \
14630 } while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, newValue) != oldValue); \
14631 (void)order; \
14632 return oldValue
14633#if defined(MA_ATOMIC_MODERN_MSVC) || defined(MA_ATOMIC_LEGACY_MSVC)
14634 #define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, replacement, order, intrin, ma_atomicType, msvcType) \
14635 ma_atomicType result; \
14636 switch (order) \
14637 { \
14638 case ma_atomic_memory_order_relaxed: \
14639 { \
14640 result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)replacement); \
14641 } break; \
14642 case ma_atomic_memory_order_consume: \
14643 case ma_atomic_memory_order_acquire: \
14644 { \
14645 result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)replacement); \
14646 } break; \
14647 case ma_atomic_memory_order_release: \
14648 { \
14649 result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)replacement); \
14650 } break; \
14651 case ma_atomic_memory_order_acq_rel: \
14652 case ma_atomic_memory_order_seq_cst: \
14653 default: \
14654 { \
14655 result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)replacement); \
14656 } break; \
14657 } \
14658 return result;
14659 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
14660 #define ma_atomic_compare_and_swap_8( dst, expected, replacement) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)replacement, (char)expected)
14661 #else
14662 static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement)
14663 {
14664 MA_ATOMIC_COMPARE_AND_SWAP_LOCK(8, dst, expected, replacement);
14665 }
14666 #endif
14667 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
14668 #define ma_atomic_compare_and_swap_16(dst, expected, replacement) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)replacement, (short)expected)
14669 #else
14670 static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement)
14671 {
14672 MA_ATOMIC_COMPARE_AND_SWAP_LOCK(16, dst, expected, replacement);
14673 }
14674 #endif
14675 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
14676 #define ma_atomic_compare_and_swap_32(dst, expected, replacement) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)replacement, (long)expected)
14677 #else
14678 static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement)
14679 {
14680 MA_ATOMIC_COMPARE_AND_SWAP_LOCK(32, dst, expected, replacement);
14681 }
14682 #endif
14683 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
14684 #define ma_atomic_compare_and_swap_64(dst, expected, replacement) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)replacement, (ma_int64)expected)
14685 #else
14686 static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement)
14687 {
14688 MA_ATOMIC_COMPARE_AND_SWAP_LOCK(64, dst, expected, replacement);
14689 }
14690 #endif
14691 static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order)
14692 {
14693 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
14694 {
14695 #if defined(MA_ARM)
14696 {
14697 MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char);
14698 }
14699 #else
14700 {
14701 (void)order;
14702 return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0);
14703 }
14704 #endif
14705 }
14706 #else
14707 {
14708 MA_ATOMIC_LOAD_EXPLICIT_LOCK(8, ptr, order);
14709 }
14710 #endif
14711 }
14712 static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order)
14713 {
14714 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
14715 {
14716 #if defined(MA_ARM)
14717 {
14718 MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short);
14719 }
14720 #else
14721 {
14722 (void)order;
14723 return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0);
14724 }
14725 #endif
14726 }
14727 #else
14728 {
14729 MA_ATOMIC_LOAD_EXPLICIT_LOCK(16, ptr, order);
14730 }
14731 #endif
14732 }
14733 static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order)
14734 {
14735 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
14736 {
14737 #if defined(MA_ARM)
14738 {
14739 MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long);
14740 }
14741 #else
14742 {
14743 (void)order;
14744 return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0);
14745 }
14746 #endif
14747 }
14748 #else
14749 {
14750 MA_ATOMIC_LOAD_EXPLICIT_LOCK(32, ptr, order);
14751 }
14752 #endif
14753 }
14754 static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order)
14755 {
14756 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
14757 {
14758 #if defined(MA_ARM)
14759 {
14760 MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long);
14761 }
14762 #else
14763 {
14764 (void)order;
14765 return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0);
14766 }
14767 #endif
14768 }
14769 #else
14770 {
14771 MA_ATOMIC_LOAD_EXPLICIT_LOCK(64, ptr, order);
14772 }
14773 #endif
14774 }
14775 static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
14776 {
14777 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
14778 {
14779 #if defined(MA_ARM)
14780 {
14781 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, ma_uint8, char);
14782 }
14783 #else
14784 {
14785 (void)order;
14786 return (ma_uint8)_InterlockedExchange8((volatile char*)dst, (char)src);
14787 }
14788 #endif
14789 }
14790 #else
14791 {
14792 MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(8, dst, src, order);
14793 }
14794 #endif
14795 }
14796 static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
14797 {
14798 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
14799 {
14800 #if defined(MA_ARM)
14801 {
14802 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, ma_uint16, short);
14803 }
14804 #else
14805 {
14806 (void)order;
14807 return (ma_uint16)_InterlockedExchange16((volatile short*)dst, (short)src);
14808 }
14809 #endif
14810 }
14811 #else
14812 {
14813 MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(16, dst, src, order);
14814 }
14815 #endif
14816 }
14817 static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
14818 {
14819 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
14820 {
14821 #if defined(MA_ARM)
14822 {
14823 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, ma_uint32, long);
14824 }
14825 #else
14826 {
14827 (void)order;
14828 return (ma_uint32)_InterlockedExchange((volatile long*)dst, (long)src);
14829 }
14830 #endif
14831 }
14832 #else
14833 {
14834 MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(32, dst, src, order);
14835 }
14836 #endif
14837 }
14838 static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
14839 {
14840 #if defined(MA_ATOMIC_IS_LOCK_FREE_64)
14841 {
14842 #if defined(MA_32BIT)
14843 {
14844 MA_ATOMIC_EXCHANGE_EXPLICIT_CAS(64, dst, src, order);
14845 }
14846 #else
14847 {
14848 #if defined(MA_ARM)
14849 {
14850 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long);
14851 }
14852 #else
14853 {
14854 (void)order;
14855 return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src);
14856 }
14857 #endif
14858 }
14859 #endif
14860 }
14861 #else
14862 {
14863 MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(64, dst, src, order);
14864 }
14865 #endif
14866 }
14867 static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
14868 {
14869 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
14870 {
14871 #if defined(MA_ARM)
14872 {
14873 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char);
14874 }
14875 #else
14876 {
14877 (void)order;
14878 return (ma_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src);
14879 }
14880 #endif
14881 }
14882 #else
14883 {
14884 MA_ATOMIC_FETCH_ADD_LOCK(8, dst, src, order);
14885 }
14886 #endif
14887 }
14888 static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
14889 {
14890 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
14891 {
14892 #if defined(MA_ARM)
14893 {
14894 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, ma_uint16, short);
14895 }
14896 #else
14897 {
14898 (void)order;
14899 return (ma_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src);
14900 }
14901 #endif
14902 }
14903 #else
14904 {
14905 MA_ATOMIC_FETCH_ADD_LOCK(16, dst, src, order);
14906 }
14907 #endif
14908 }
14909 static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
14910 {
14911 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
14912 {
14913 #if defined(MA_ARM)
14914 {
14915 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, ma_uint32, long);
14916 }
14917 #else
14918 {
14919 (void)order;
14920 return (ma_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src);
14921 }
14922 #endif
14923 }
14924 #else
14925 {
14926 MA_ATOMIC_FETCH_ADD_LOCK(32, dst, src, order);
14927 }
14928 #endif
14929 }
14930 static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
14931 {
14932 #if defined(MA_ATOMIC_IS_LOCK_FREE_64)
14933 {
14934 #if defined(MA_32BIT)
14935 {
14936 MA_ATOMIC_FETCH_ADD_CAS(64, dst, src, order);
14937 }
14938 #else
14939 {
14940 #if defined(MA_ARM)
14941 {
14942 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long);
14943 }
14944 #else
14945 {
14946 (void)order;
14947 return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src);
14948 }
14949 #endif
14950 }
14951 #endif
14952 }
14953 #else
14954 {
14955 MA_ATOMIC_FETCH_ADD_LOCK(64, dst, src, order);
14956 }
14957 #endif
14958 }
14959 static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
14960 {
14961 return ma_atomic_fetch_add_explicit_8(dst, (ma_uint8)(-(ma_int8)src), order);
14962 }
14963 static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
14964 {
14965 return ma_atomic_fetch_add_explicit_16(dst, (ma_uint16)(-(ma_int16)src), order);
14966 }
14967 static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
14968 {
14969 return ma_atomic_fetch_add_explicit_32(dst, (ma_uint32)(-(ma_int32)src), order);
14970 }
14971 static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
14972 {
14973 return ma_atomic_fetch_add_explicit_64(dst, (ma_uint64)(-(ma_int64)src), order);
14974 }
14975 static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
14976 {
14977 #if defined(MA_ARM)
14978 {
14979 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char);
14980 }
14981 #else
14982 {
14983 MA_ATOMIC_FETCH_AND_CAS(8, dst, src, order);
14984 }
14985 #endif
14986 }
14987 static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
14988 {
14989 #if defined(MA_ARM)
14990 {
14991 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short);
14992 }
14993 #else
14994 {
14995 MA_ATOMIC_FETCH_AND_CAS(16, dst, src, order);
14996 }
14997 #endif
14998 }
14999 static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
15000 {
15001 #if defined(MA_ARM)
15002 {
15003 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long);
15004 }
15005 #else
15006 {
15007 MA_ATOMIC_FETCH_AND_CAS(32, dst, src, order);
15008 }
15009 #endif
15010 }
15011 static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
15012 {
15013 #if defined(MA_ARM)
15014 {
15015 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long);
15016 }
15017 #else
15018 {
15019 MA_ATOMIC_FETCH_AND_CAS(64, dst, src, order);
15020 }
15021 #endif
15022 }
15023 static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
15024 {
15025 #if defined(MA_ARM)
15026 {
15027 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char);
15028 }
15029 #else
15030 {
15031 MA_ATOMIC_FETCH_OR_CAS(8, dst, src, order);
15032 }
15033 #endif
15034 }
15035 static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
15036 {
15037 #if defined(MA_ARM)
15038 {
15039 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short);
15040 }
15041 #else
15042 {
15043 MA_ATOMIC_FETCH_OR_CAS(16, dst, src, order);
15044 }
15045 #endif
15046 }
15047 static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
15048 {
15049 #if defined(MA_ARM)
15050 {
15051 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long);
15052 }
15053 #else
15054 {
15055 MA_ATOMIC_FETCH_OR_CAS(32, dst, src, order);
15056 }
15057 #endif
15058 }
15059 static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
15060 {
15061 #if defined(MA_ARM)
15062 {
15063 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long);
15064 }
15065 #else
15066 {
15067 MA_ATOMIC_FETCH_OR_CAS(64, dst, src, order);
15068 }
15069 #endif
15070 }
15071 static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
15072 {
15073 #if defined(MA_ARM)
15074 {
15075 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char);
15076 }
15077 #else
15078 {
15079 MA_ATOMIC_FETCH_XOR_CAS(8, dst, src, order);
15080 }
15081 #endif
15082 }
15083 static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
15084 {
15085 #if defined(MA_ARM)
15086 {
15087 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short);
15088 }
15089 #else
15090 {
15091 MA_ATOMIC_FETCH_XOR_CAS(16, dst, src, order);
15092 }
15093 #endif
15094 }
15095 static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
15096 {
15097 #if defined(MA_ARM)
15098 {
15099 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long);
15100 }
15101 #else
15102 {
15103 MA_ATOMIC_FETCH_XOR_CAS(32, dst, src, order);
15104 }
15105 #endif
15106 }
15107 static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
15108 {
15109 #if defined(MA_ARM)
15110 {
15111 MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long);
15112 }
15113 #else
15114 {
15115 MA_ATOMIC_FETCH_XOR_CAS(64, dst, src, order);
15116 }
15117 #endif
15118 }
15119 #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order)
15120 #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order)
15121 #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order)
15122 #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order)
15123 #if defined(MA_X64)
15124 #define ma_atomic_thread_fence(order) __faststorefence(), (void)order
15125 #elif defined(MA_ARM64)
15126 #define ma_atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order
15127 #else
15128 static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order)
15129 {
15130 volatile ma_uint32 barrier = 0;
15131 ma_atomic_fetch_add_explicit_32(&barrier, 0, order);
15132 }
15133 #endif
15134 #define ma_atomic_signal_fence(order) _ReadWriteBarrier(), (void)order
15135#endif
15136#if defined(MA_ATOMIC_LEGACY_MSVC_ASM)
15137 static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement)
15138 {
15139 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
15140 {
15141 ma_uint8 result = 0;
15142 __asm {
15143 mov ecx, dst
15144 mov al, expected
15145 mov dl, replacement
15146 lock cmpxchg [ecx], dl
15147 mov result, al
15148 }
15149 return result;
15150 }
15151 #else
15152 {
15153 MA_ATOMIC_COMPARE_AND_SWAP_LOCK(8, dst, expected, replacement);
15154 }
15155 #endif
15156 }
15157 static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement)
15158 {
15159 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
15160 {
15161 ma_uint16 result = 0;
15162 __asm {
15163 mov ecx, dst
15164 mov ax, expected
15165 mov dx, replacement
15166 lock cmpxchg [ecx], dx
15167 mov result, ax
15168 }
15169 return result;
15170 }
15171 #else
15172 {
15173 MA_ATOMIC_COMPARE_AND_SWAP_LOCK(16, dst, expected, replacement);
15174 }
15175 #endif
15176 }
15177 static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement)
15178 {
15179 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
15180 {
15181 ma_uint32 result = 0;
15182 __asm {
15183 mov ecx, dst
15184 mov eax, expected
15185 mov edx, replacement
15186 lock cmpxchg [ecx], edx
15187 mov result, eax
15188 }
15189 return result;
15190 }
15191 #else
15192 {
15193 MA_ATOMIC_COMPARE_AND_SWAP_LOCK(32, dst, expected, replacement);
15194 }
15195 #endif
15196 }
15197 static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement)
15198 {
15199 #if defined(MA_ATOMIC_IS_LOCK_FREE_64)
15200 {
15201 ma_uint32 resultEAX = 0;
15202 ma_uint32 resultEDX = 0;
15203 __asm {
15204 mov esi, dst
15205 mov eax, dword ptr expected
15206 mov edx, dword ptr expected + 4
15207 mov ebx, dword ptr replacement
15208 mov ecx, dword ptr replacement + 4
15209 lock cmpxchg8b qword ptr [esi]
15210 mov resultEAX, eax
15211 mov resultEDX, edx
15212 }
15213 return ((ma_uint64)resultEDX << 32) | resultEAX;
15214 }
15215 #else
15216 {
15217 MA_ATOMIC_COMPARE_AND_SWAP_LOCK(64, dst, expected, replacement);
15218 }
15219 #endif
15220 }
15221 static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* dst, ma_atomic_memory_order order)
15222 {
15223 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
15224 {
15225 ma_uint8 result = 0;
15226 if (order == ma_atomic_memory_order_relaxed) {
15227 __asm {
15228 mov esi, dst
15229 mov al, [esi]
15230 mov result, al
15231 }
15232 } else if (order <= ma_atomic_memory_order_release) {
15233 __asm {
15234 mov esi, dst
15235 mov al, [esi]
15236 lock add dword ptr [esp], 0
15237 mov result, al
15238 }
15239 } else {
15240 __asm {
15241 lock add dword ptr [esp], 0
15242 mov esi, dst
15243 mov al, [esi]
15244 mov result, al
15245 lock add dword ptr [esp], 0
15246 }
15247 }
15248 return result;
15249 }
15250 #else
15251 {
15252 MA_ATOMIC_LOAD_EXPLICIT_LOCK(8, dst, order);
15253 }
15254 #endif
15255 }
15256 static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* dst, ma_atomic_memory_order order)
15257 {
15258 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
15259 {
15260 ma_uint16 result = 0;
15261 if (order == ma_atomic_memory_order_relaxed) {
15262 __asm {
15263 mov esi, dst
15264 mov ax, [esi]
15265 mov result, ax
15266 }
15267 } else if (order <= ma_atomic_memory_order_release) {
15268 __asm {
15269 mov esi, dst
15270 mov ax, [esi]
15271 lock add dword ptr [esp], 0
15272 mov result, ax
15273 }
15274 } else {
15275 __asm {
15276 lock add dword ptr [esp], 0
15277 mov esi, dst
15278 mov ax, [esi]
15279 mov result, ax
15280 lock add dword ptr [esp], 0
15281 }
15282 }
15283 return result;
15284 }
15285 #else
15286 {
15287 MA_ATOMIC_LOAD_EXPLICIT_LOCK(16, dst, order);
15288 }
15289 #endif
15290 }
15291 static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* dst, ma_atomic_memory_order order)
15292 {
15293 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
15294 {
15295 ma_uint32 result = 0;
15296 if (order == ma_atomic_memory_order_relaxed) {
15297 __asm {
15298 mov esi, dst
15299 mov eax, [esi]
15300 mov result, eax
15301 }
15302 } else if (order <= ma_atomic_memory_order_release) {
15303 __asm {
15304 mov esi, dst
15305 mov eax, [esi]
15306 lock add dword ptr [esp], 0
15307 mov result, eax
15308 }
15309 } else {
15310 __asm {
15311 lock add dword ptr [esp], 0
15312 mov esi, dst
15313 mov eax, [esi]
15314 mov result, eax
15315 lock add dword ptr [esp], 0
15316 }
15317 }
15318 return result;
15319 }
15320 #else
15321 {
15322 MA_ATOMIC_LOAD_EXPLICIT_LOCK(32, dst, order);
15323 }
15324 #endif
15325 }
15326 static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* dst, ma_atomic_memory_order order)
15327 {
15328 (void)order;
15329 return ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, 0, 0);
15330 }
15331 static MA_INLINE void __stdcall ma_atomic_store_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
15332 {
15333 if (order == ma_atomic_memory_order_relaxed) {
15334 __asm {
15335 mov esi, dst
15336 mov al, src
15337 mov [esi], al
15338 }
15339 } else {
15340 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
15341 {
15342 __asm {
15343 mov esi, dst
15344 mov al, src
15345 xchg [esi], al
15346 }
15347 }
15348 #else
15349 {
15350 MA_ATOMIC_STORE_EXPLICIT_LOCK(8, dst, src, order);
15351 }
15352 #endif
15353 }
15354 }
15355 static MA_INLINE void __stdcall ma_atomic_store_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
15356 {
15357 if (order == ma_atomic_memory_order_relaxed) {
15358 __asm {
15359 mov esi, dst
15360 mov ax, src
15361 mov [esi], ax
15362 }
15363 } else {
15364 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
15365 {
15366 __asm {
15367 mov esi, dst
15368 mov ax, src
15369 xchg [esi], ax
15370 }
15371 }
15372 #else
15373 {
15374 MA_ATOMIC_STORE_EXPLICIT_LOCK(16, dst, src, order);
15375 }
15376 #endif
15377 }
15378 }
15379 static MA_INLINE void __stdcall ma_atomic_store_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
15380 {
15381 if (order == ma_atomic_memory_order_relaxed) {
15382 __asm {
15383 mov esi, dst
15384 mov eax, src
15385 mov [esi], eax
15386 }
15387 } else {
15388 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
15389 {
15390 __asm {
15391 mov esi, dst
15392 mov eax, src
15393 xchg [esi], eax
15394 }
15395 }
15396 #else
15397 {
15398 MA_ATOMIC_STORE_EXPLICIT_LOCK(32, dst, src, order);
15399 }
15400 #endif
15401 }
15402 }
15403 static MA_INLINE void __stdcall ma_atomic_store_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
15404 {
15405 #if defined(MA_ATOMIC_IS_LOCK_FREE_64)
15406 {
15407 MA_ATOMIC_STORE_EXPLICIT_CAS(64, dst, src, order);
15408 }
15409 #else
15410 {
15411 MA_ATOMIC_STORE_EXPLICIT_LOCK(64, dst, src, order);
15412 }
15413 #endif
15414 }
15415 static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
15416 {
15417 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
15418 {
15419 ma_uint8 result = 0;
15420 (void)order;
15421 __asm {
15422 mov ecx, dst
15423 mov al, src
15424 lock xchg [ecx], al
15425 mov result, al
15426 }
15427 return result;
15428 }
15429 #else
15430 {
15431 MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(8, dst, src, order);
15432 }
15433 #endif
15434 }
15435 static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
15436 {
15437 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
15438 {
15439 ma_uint16 result = 0;
15440 (void)order;
15441 __asm {
15442 mov ecx, dst
15443 mov ax, src
15444 lock xchg [ecx], ax
15445 mov result, ax
15446 }
15447 return result;
15448 }
15449 #else
15450 {
15451 MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(16, dst, src, order);
15452 }
15453 #endif
15454 }
15455 static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
15456 {
15457 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
15458 {
15459 ma_uint32 result = 0;
15460 (void)order;
15461 __asm {
15462 mov ecx, dst
15463 mov eax, src
15464 xchg [ecx], eax
15465 mov result, eax
15466 }
15467 return result;
15468 }
15469 #else
15470 {
15471 MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(32, dst, src, order);
15472 }
15473 #endif
15474 }
15475 static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
15476 {
15477 #if defined(MA_ATOMIC_IS_LOCK_FREE_64)
15478 {
15479 MA_ATOMIC_EXCHANGE_EXPLICIT_CAS(64, dst, src, order);
15480 }
15481 #else
15482 {
15483 MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(64, dst, src, order);
15484 }
15485 #endif
15486 }
15487 static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
15488 {
15489 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
15490 {
15491 ma_uint8 result = 0;
15492 (void)order;
15493 __asm {
15494 mov ecx, dst
15495 mov al, src
15496 lock xadd [ecx], al
15497 mov result, al
15498 }
15499 return result;
15500 }
15501 #else
15502 {
15503 MA_ATOMIC_FETCH_ADD_LOCK(8, dst, src, order);
15504 }
15505 #endif
15506 }
15507 static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
15508 {
15509 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
15510 {
15511 ma_uint16 result = 0;
15512 (void)order;
15513 __asm {
15514 mov ecx, dst
15515 mov ax, src
15516 lock xadd [ecx], ax
15517 mov result, ax
15518 }
15519 return result;
15520 }
15521 #else
15522 {
15523 MA_ATOMIC_FETCH_ADD_LOCK(16, dst, src, order);
15524 }
15525 #endif
15526 }
15527 static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
15528 {
15529 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
15530 {
15531 ma_uint32 result = 0;
15532 (void)order;
15533 __asm {
15534 mov ecx, dst
15535 mov eax, src
15536 lock xadd [ecx], eax
15537 mov result, eax
15538 }
15539 return result;
15540 }
15541 #else
15542 {
15543 MA_ATOMIC_FETCH_ADD_LOCK(32, dst, src, order);
15544 }
15545 #endif
15546 }
15547 static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
15548 {
15549 #if defined(MA_ATOMIC_IS_LOCK_FREE_64)
15550 {
15551 MA_ATOMIC_FETCH_ADD_CAS(64, dst, src, order);
15552 }
15553 #else
15554 {
15555 MA_ATOMIC_FETCH_ADD_LOCK(64, dst, src, order);
15556 }
15557 #endif
15558 }
15559 static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
15560 {
15561 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
15562 {
15563 ma_uint8 result = 0;
15564 (void)order;
15565 __asm {
15566 mov ecx, dst
15567 mov al, src
15568 neg al
15569 lock xadd [ecx], al
15570 mov result, al
15571 }
15572 return result;
15573 }
15574 #else
15575 {
15576 MA_ATOMIC_FETCH_ADD_LOCK(8, dst, (ma_uint8)(-(ma_int8)src), order);
15577 }
15578 #endif
15579 }
15580 static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
15581 {
15582 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
15583 {
15584 ma_uint16 result = 0;
15585 (void)order;
15586 __asm {
15587 mov ecx, dst
15588 mov ax, src
15589 neg ax
15590 lock xadd [ecx], ax
15591 mov result, ax
15592 }
15593 return result;
15594 }
15595 #else
15596 {
15597 MA_ATOMIC_FETCH_ADD_LOCK(16, dst, (ma_uint16)(-(ma_int16)src), order);
15598 }
15599 #endif
15600 }
15601 static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
15602 {
15603 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
15604 {
15605 ma_uint32 result = 0;
15606 (void)order;
15607 __asm {
15608 mov ecx, dst
15609 mov eax, src
15610 neg eax
15611 lock xadd [ecx], eax
15612 mov result, eax
15613 }
15614 return result;
15615 }
15616 #else
15617 {
15618 MA_ATOMIC_FETCH_ADD_LOCK(32, dst, (ma_uint32)(-(ma_int32)src), order);
15619 }
15620 #endif
15621 }
15622 static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
15623 {
15624 MA_ATOMIC_FETCH_ADD_CAS(64, dst, (ma_uint64)(-(ma_int64)src), order);
15625 }
15626 static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
15627 {
15628 MA_ATOMIC_FETCH_AND_CAS(8, dst, src, order);
15629 }
15630 static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
15631 {
15632 MA_ATOMIC_FETCH_AND_CAS(16, dst, src, order);
15633 }
15634 static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
15635 {
15636 MA_ATOMIC_FETCH_AND_CAS(32, dst, src, order);
15637 }
15638 static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
15639 {
15640 MA_ATOMIC_FETCH_AND_CAS(64, dst, src, order);
15641 }
15642 static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
15643 {
15644 MA_ATOMIC_FETCH_OR_CAS(8, dst, src, order);
15645 }
15646 static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
15647 {
15648 MA_ATOMIC_FETCH_OR_CAS(16, dst, src, order);
15649 }
15650 static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
15651 {
15652 MA_ATOMIC_FETCH_OR_CAS(32, dst, src, order);
15653 }
15654 static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
15655 {
15656 MA_ATOMIC_FETCH_OR_CAS(64, dst, src, order);
15657 }
15658 static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
15659 {
15660 MA_ATOMIC_FETCH_XOR_CAS(8, dst, src, order);
15661 }
15662 static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
15663 {
15664 MA_ATOMIC_FETCH_XOR_CAS(16, dst, src, order);
15665 }
15666 static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
15667 {
15668 MA_ATOMIC_FETCH_XOR_CAS(32, dst, src, order);
15669 }
15670 static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
15671 {
15672 MA_ATOMIC_FETCH_XOR_CAS(64, dst, src, order);
15673 }
15674 static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order)
15675 {
15676 (void)order;
15677 __asm {
15678 lock add dword ptr [esp], 0
15679 }
15680 }
15681 #define ma_atomic_signal_fence(order) __asm {}; (void)order
15682#endif
15683#if defined(MA_ATOMIC_MODERN_GCC)
15684 #define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE
15685 #define ma_atomic_thread_fence(order) __atomic_thread_fence(order)
15686 #define ma_atomic_signal_fence(order) __atomic_signal_fence(order)
15687 #define ma_atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr)
15688 #define ma_atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr)
15689 #define ma_atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr)
15690 #define ma_atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr)
15691 #define ma_atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order)
15692 #define ma_atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order)
15693 #define ma_atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order)
15694 #define ma_atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order)
15695 #define ma_atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order)
15696 #define ma_atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order)
15697 #define ma_atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order)
15698 #define ma_atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order)
15699 #define ma_atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order)
15700 #define ma_atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order)
15701 #define ma_atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order)
15702 #define ma_atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order)
15703 #define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 0, successOrder, failureOrder)
15704 #define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 0, successOrder, failureOrder)
15705 #define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 0, successOrder, failureOrder)
15706 #define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 0, successOrder, failureOrder)
15707 #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 1, successOrder, failureOrder)
15708 #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 1, successOrder, failureOrder)
15709 #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 1, successOrder, failureOrder)
15710 #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 1, successOrder, failureOrder)
15711 #define ma_atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order)
15712 #define ma_atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order)
15713 #define ma_atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order)
15714 #define ma_atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order)
15715 #define ma_atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order)
15716 #define ma_atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order)
15717 #define ma_atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order)
15718 #define ma_atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order)
15719 #define ma_atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order)
15720 #define ma_atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order)
15721 #define ma_atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order)
15722 #define ma_atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order)
15723 #define ma_atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order)
15724 #define ma_atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order)
15725 #define ma_atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order)
15726 #define ma_atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order)
15727 #define ma_atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order)
15728 #define ma_atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order)
15729 #define ma_atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order)
15730 #define ma_atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order)
15731 static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement)
15732 {
15733 __atomic_compare_exchange_n(dst, &expected, replacement, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
15734 return expected;
15735 }
15736 static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement)
15737 {
15738 __atomic_compare_exchange_n(dst, &expected, replacement, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
15739 return expected;
15740 }
15741 static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement)
15742 {
15743 __atomic_compare_exchange_n(dst, &expected, replacement, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
15744 return expected;
15745 }
15746 #if defined(__clang__)
15747 #pragma clang diagnostic push
15748 #if __clang_major__ >= 8
15749 #pragma clang diagnostic ignored "-Watomic-alignment"
15750 #endif
15751 #endif
15752 static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement)
15753 {
15754 __atomic_compare_exchange_n(dst, &expected, replacement, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
15755 return expected;
15756 }
15757 #if defined(__clang__)
15758 #pragma clang diagnostic pop
15759 #endif
15760#endif
15761#if defined(MA_ATOMIC_LEGACY_GCC) || defined(MA_ATOMIC_LEGACY_GCC_ASM)
15762 #define ma_atomic_signal_fence(order) __asm__ __volatile__("":::"memory")
15763 #if defined(MA_ATOMIC_LEGACY_GCC)
15764 #define ma_atomic_thread_fence(order) __sync_synchronize(), (void)order
15765 static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement)
15766 {
15767 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
15768 {
15769 return __sync_val_compare_and_swap(dst, expected, replacement);
15770 }
15771 #else
15772 {
15773 MA_ATOMIC_COMPARE_AND_SWAP_LOCK(8, dst, expected, replacement);
15774 }
15775 #endif
15776 }
15777 static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement)
15778 {
15779 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
15780 {
15781 return __sync_val_compare_and_swap(dst, expected, replacement);
15782 }
15783 #else
15784 {
15785 MA_ATOMIC_COMPARE_AND_SWAP_LOCK(16, dst, expected, replacement);
15786 }
15787 #endif
15788 }
15789 static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement)
15790 {
15791 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
15792 {
15793 return __sync_val_compare_and_swap(dst, expected, replacement);
15794 }
15795 #else
15796 {
15797 MA_ATOMIC_COMPARE_AND_SWAP_LOCK(32, dst, expected, replacement);
15798 }
15799 #endif
15800 }
15801 static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement)
15802 {
15803 #if defined(MA_ATOMIC_IS_LOCK_FREE_64)
15804 {
15805 return __sync_val_compare_and_swap(dst, expected, replacement);
15806 }
15807 #else
15808 {
15809 MA_ATOMIC_COMPARE_AND_SWAP_LOCK(64, dst, expected, replacement);
15810 }
15811 #endif
15812 }
15813 static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order)
15814 {
15815 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
15816 {
15817 (void)order;
15818 return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0);
15819 }
15820 #else
15821 {
15822 MA_ATOMIC_LOAD_EXPLICIT_LOCK(8, ptr, order);
15823 }
15824 #endif
15825 }
15826 static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order)
15827 {
15828 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
15829 {
15830 (void)order;
15831 return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0);
15832 }
15833 #else
15834 {
15835 MA_ATOMIC_LOAD_EXPLICIT_LOCK(16, ptr, order);
15836 }
15837 #endif
15838 }
15839 static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order)
15840 {
15841 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
15842 {
15843 (void)order;
15844 return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0);
15845 }
15846 #else
15847 {
15848 MA_ATOMIC_LOAD_EXPLICIT_LOCK(32, ptr, order);
15849 }
15850 #endif
15851 }
15852 static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order)
15853 {
15854 #if defined(MA_ATOMIC_IS_LOCK_FREE_64)
15855 {
15856 (void)order;
15857 return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0);
15858 }
15859 #else
15860 {
15861 MA_ATOMIC_LOAD_EXPLICIT_LOCK(64, ptr, order);
15862 }
15863 #endif
15864 }
15865 static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
15866 {
15867 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
15868 {
15869 if (order > ma_atomic_memory_order_acquire) {
15870 __sync_synchronize();
15871 }
15872 return __sync_lock_test_and_set(dst, src);
15873 }
15874 #else
15875 {
15876 MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(8, dst, src, order);
15877 }
15878 #endif
15879 }
15880 static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
15881 {
15882 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
15883 {
15884 if (order > ma_atomic_memory_order_acquire) {
15885 __sync_synchronize();
15886 }
15887 return __sync_lock_test_and_set(dst, src);
15888 }
15889 #else
15890 {
15891 MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(16, dst, src, order);
15892 }
15893 #endif
15894 }
15895 static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
15896 {
15897 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
15898 {
15899 if (order > ma_atomic_memory_order_acquire) {
15900 __sync_synchronize();
15901 }
15902 return __sync_lock_test_and_set(dst, src);
15903 }
15904 #else
15905 {
15906 MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(32, dst, src, order);
15907 }
15908 #endif
15909 }
15910 static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
15911 {
15912 #if defined(MA_ATOMIC_IS_LOCK_FREE_64)
15913 {
15914 if (order > ma_atomic_memory_order_acquire) {
15915 __sync_synchronize();
15916 }
15917 return __sync_lock_test_and_set(dst, src);
15918 }
15919 #else
15920 {
15921 MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(64, dst, src, order);
15922 }
15923 #endif
15924 }
15925 #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order)
15926 #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order)
15927 #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order)
15928 #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order)
15929 static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
15930 {
15931 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
15932 {
15933 (void)order;
15934 return __sync_fetch_and_add(dst, src);
15935 }
15936 #else
15937 {
15938 MA_ATOMIC_FETCH_ADD_LOCK(8, dst, src, order);
15939 }
15940 #endif
15941 }
15942 static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
15943 {
15944 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
15945 {
15946 (void)order;
15947 return __sync_fetch_and_add(dst, src);
15948 }
15949 #else
15950 {
15951 MA_ATOMIC_FETCH_ADD_LOCK(16, dst, src, order);
15952 }
15953 #endif
15954 }
15955 static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
15956 {
15957 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
15958 {
15959 (void)order;
15960 return __sync_fetch_and_add(dst, src);
15961 }
15962 #else
15963 {
15964 MA_ATOMIC_FETCH_ADD_LOCK(32, dst, src, order);
15965 }
15966 #endif
15967 }
15968 static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
15969 {
15970 #if defined(MA_ATOMIC_IS_LOCK_FREE_64)
15971 {
15972 (void)order;
15973 return __sync_fetch_and_add(dst, src);
15974 }
15975 #else
15976 {
15977 MA_ATOMIC_FETCH_ADD_LOCK(64, dst, src, order);
15978 }
15979 #endif
15980 }
15981 static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
15982 {
15983 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
15984 {
15985 (void)order;
15986 return __sync_fetch_and_sub(dst, src);
15987 }
15988 #else
15989 {
15990 MA_ATOMIC_FETCH_ADD_LOCK(8, dst, (ma_uint8)(-(ma_int8)src), order);
15991 }
15992 #endif
15993 }
15994 static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
15995 {
15996 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
15997 {
15998 (void)order;
15999 return __sync_fetch_and_sub(dst, src);
16000 }
16001 #else
16002 {
16003 MA_ATOMIC_FETCH_ADD_LOCK(16, dst, (ma_uint16)(-(ma_int16)src), order);
16004 }
16005 #endif
16006 }
16007 static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
16008 {
16009 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
16010 {
16011 (void)order;
16012 return __sync_fetch_and_sub(dst, src);
16013 }
16014 #else
16015 {
16016 MA_ATOMIC_FETCH_ADD_LOCK(32, dst, (ma_uint32)(-(ma_int32)src), order);
16017 }
16018 #endif
16019 }
16020 static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
16021 {
16022 #if defined(MA_ATOMIC_IS_LOCK_FREE_64)
16023 {
16024 (void)order;
16025 return __sync_fetch_and_sub(dst, src);
16026 }
16027 #else
16028 {
16029 MA_ATOMIC_FETCH_ADD_LOCK(64, dst, (ma_uint64)(-(ma_int64)src), order);
16030 }
16031 #endif
16032 }
16033 static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
16034 {
16035 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
16036 {
16037 (void)order;
16038 return __sync_fetch_and_and(dst, src);
16039 }
16040 #else
16041 {
16042 MA_ATOMIC_FETCH_AND_CAS(8, dst, src, order);
16043 }
16044 #endif
16045 }
16046 static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
16047 {
16048 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
16049 {
16050 (void)order;
16051 return __sync_fetch_and_and(dst, src);
16052 }
16053 #else
16054 {
16055 MA_ATOMIC_FETCH_AND_CAS(16, dst, src, order);
16056 }
16057 #endif
16058 }
16059 static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
16060 {
16061 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
16062 {
16063 (void)order;
16064 return __sync_fetch_and_and(dst, src);
16065 }
16066 #else
16067 {
16068 MA_ATOMIC_FETCH_AND_CAS(32, dst, src, order);
16069 }
16070 #endif
16071 }
16072 static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
16073 {
16074 #if defined(MA_ATOMIC_IS_LOCK_FREE_64)
16075 {
16076 (void)order;
16077 return __sync_fetch_and_and(dst, src);
16078 }
16079 #else
16080 {
16081 MA_ATOMIC_FETCH_AND_CAS(64, dst, src, order);
16082 }
16083 #endif
16084 }
16085 static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
16086 {
16087 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
16088 {
16089 (void)order;
16090 return __sync_fetch_and_or(dst, src);
16091 }
16092 #else
16093 {
16094 MA_ATOMIC_FETCH_OR_CAS(8, dst, src, order);
16095 }
16096 #endif
16097 }
16098 static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
16099 {
16100 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
16101 {
16102 (void)order;
16103 return __sync_fetch_and_or(dst, src);
16104 }
16105 #else
16106 {
16107 MA_ATOMIC_FETCH_OR_CAS(16, dst, src, order);
16108 }
16109 #endif
16110 }
16111 static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
16112 {
16113 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
16114 {
16115 (void)order;
16116 return __sync_fetch_and_or(dst, src);
16117 }
16118 #else
16119 {
16120 MA_ATOMIC_FETCH_OR_CAS(32, dst, src, order);
16121 }
16122 #endif
16123 }
16124 static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
16125 {
16126 #if defined(MA_ATOMIC_IS_LOCK_FREE_64)
16127 {
16128 (void)order;
16129 return __sync_fetch_and_or(dst, src);
16130 }
16131 #else
16132 {
16133 MA_ATOMIC_FETCH_OR_CAS(64, dst, src, order);
16134 }
16135 #endif
16136 }
16137 static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
16138 {
16139 #if defined(MA_ATOMIC_IS_LOCK_FREE_8)
16140 {
16141 (void)order;
16142 return __sync_fetch_and_xor(dst, src);
16143 }
16144 #else
16145 {
16146 MA_ATOMIC_FETCH_XOR_CAS(8, dst, src, order);
16147 }
16148 #endif
16149 }
16150 static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
16151 {
16152 #if defined(MA_ATOMIC_IS_LOCK_FREE_16)
16153 {
16154 (void)order;
16155 return __sync_fetch_and_xor(dst, src);
16156 }
16157 #else
16158 {
16159 MA_ATOMIC_FETCH_XOR_CAS(16, dst, src, order);
16160 }
16161 #endif
16162 }
16163 static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
16164 {
16165 #if defined(MA_ATOMIC_IS_LOCK_FREE_32)
16166 {
16167 (void)order;
16168 return __sync_fetch_and_xor(dst, src);
16169 }
16170 #else
16171 {
16172 MA_ATOMIC_FETCH_XOR_CAS(32, dst, src, order);
16173 }
16174 #endif
16175 }
16176 static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
16177 {
16178 #if defined(MA_ATOMIC_IS_LOCK_FREE_64)
16179 {
16180 (void)order;
16181 return __sync_fetch_and_xor(dst, src);
16182 }
16183 #else
16184 {
16185 MA_ATOMIC_FETCH_XOR_CAS(64, dst, src, order);
16186 }
16187 #endif
16188 }
16189 #elif defined(MA_ATOMIC_LEGACY_GCC_ASM)
16190 #define MA_ATOMIC_CMPXCHG_GCC_X86(instructionSizeSuffix, result, dst, expected, replacement) \
16191 __asm__ __volatile__( \
16192 "lock; cmpxchg"instructionSizeSuffix" %2, %1" \
16193 : "=a"(result), \
16194 "=m"(*dst) \
16195 : "r"(replacement), \
16196 "0"(expected), \
16197 "m"(*dst) \
16198 : "cc", "memory")
16199 #define MA_ATOMIC_XADD_GCC_X86(instructionSizeSuffix, result, dst, src) \
16200 __asm__ __volatile__( \
16201 "lock; xadd"instructionSizeSuffix" %0, %1" \
16202 : "=a"(result), \
16203 "=m"(*dst) \
16204 : "0"(src), \
16205 "m"(*dst) \
16206 : "cc", "memory")
16207 static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement)
16208 {
16209 #if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64))
16210 {
16211 ma_uint8 result;
16212 #if defined(MA_X86) || defined(MA_X64)
16213 {
16214 MA_ATOMIC_CMPXCHG_GCC_X86("b", result, dst, expected, replacement);
16215 }
16216 #else
16217 {
16218 #error Unsupported architecture.
16219 }
16220 #endif
16221 return result;
16222 }
16223 #else
16224 {
16225 MA_ATOMIC_COMPARE_AND_SWAP_LOCK(8, dst, expected, replacement);
16226 }
16227 #endif
16228 }
16229 static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement)
16230 {
16231 #if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64))
16232 {
16233 ma_uint16 result;
16234 #if defined(MA_X86) || defined(MA_X64)
16235 {
16236 MA_ATOMIC_CMPXCHG_GCC_X86("w", result, dst, expected, replacement);
16237 }
16238 #else
16239 {
16240 #error Unsupported architecture.
16241 }
16242 #endif
16243 return result;
16244 }
16245 #else
16246 {
16247 MA_ATOMIC_COMPARE_AND_SWAP_LOCK(16, dst, expected, replacement);
16248 }
16249 #endif
16250 }
16251 static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement)
16252 {
16253 #if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64))
16254 {
16255 ma_uint32 result;
16256 #if defined(MA_X86) || defined(MA_X64)
16257 {
16258 MA_ATOMIC_CMPXCHG_GCC_X86("l", result, dst, expected, replacement);
16259 }
16260 #else
16261 {
16262 #error Unsupported architecture.
16263 }
16264 #endif
16265 return result;
16266 }
16267 #else
16268 {
16269 MA_ATOMIC_COMPARE_AND_SWAP_LOCK(32, dst, expected, replacement);
16270 }
16271 #endif
16272 }
16273 static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement)
16274 {
16275 #if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64))
16276 {
16277 ma_uint64 result;
16278 #if defined(MA_X86)
16279 {
16280 ma_uint32 resultEAX;
16281 ma_uint32 resultEDX;
16282 __asm__ __volatile__(
16283 "pushl %%ebx\n"
16284 "movl %4, %%ebx\n"
16285 "lock cmpxchg8b (%%edi)\n"
16286 "popl %%ebx\n"
16287 : "=a"(resultEAX),
16288 "=d"(resultEDX)
16289 : "a"((ma_uint32)(expected & 0xFFFFFFFF)),
16290 "d"((ma_uint32)(expected >> 32)),
16291 "r"((ma_uint32)(replacement & 0xFFFFFFFF)),
16292 "c"((ma_uint32)(replacement >> 32)),
16293 "D"(dst)
16294 : "memory", "cc");
16295 result = ((ma_uint64)resultEDX << 32) | resultEAX;
16296 }
16297 #elif defined(MA_X64)
16298 {
16299 MA_ATOMIC_CMPXCHG_GCC_X86("q", result, dst, expected, replacement);
16300 }
16301 #else
16302 {
16303 #error Unsupported architecture.
16304 }
16305 #endif
16306 return result;
16307 }
16308 #else
16309 {
16310 MA_ATOMIC_COMPARE_AND_SWAP_LOCK(64, dst, expected, replacement);
16311 }
16312 #endif
16313 }
16314 static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* dst, ma_atomic_memory_order order)
16315 {
16316 #if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64))
16317 {
16318 ma_uint8 result;
16319 #if defined(MA_X86) || defined(MA_X64)
16320 {
16321 if (order == ma_atomic_memory_order_relaxed) {
16322 MA_ATOMIC_LOAD_RELAXED_GCC_X86("b", result, dst);
16323 } else if (order <= ma_atomic_memory_order_release) {
16324 MA_ATOMIC_LOAD_RELEASE_GCC_X86("b", result, dst);
16325 } else {
16326 MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("b", result, dst);
16327 }
16328 }
16329 #else
16330 {
16331 #error Unsupported architecture.
16332 }
16333 #endif
16334 return result;
16335 }
16336 #else
16337 {
16338 MA_ATOMIC_LOAD_EXPLICIT_LOCK(8, dst, order);
16339 }
16340 #endif
16341 }
16342 static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* dst, ma_atomic_memory_order order)
16343 {
16344 #if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64))
16345 {
16346 ma_uint16 result;
16347 #if defined(MA_X86) || defined(MA_X64)
16348 {
16349 if (order == ma_atomic_memory_order_relaxed) {
16350 MA_ATOMIC_LOAD_RELAXED_GCC_X86("w", result, dst);
16351 } else if (order <= ma_atomic_memory_order_release) {
16352 MA_ATOMIC_LOAD_RELEASE_GCC_X86("w", result, dst);
16353 } else {
16354 MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("w", result, dst);
16355 }
16356 }
16357 #else
16358 {
16359 #error Unsupported architecture.
16360 }
16361 #endif
16362 return result;
16363 }
16364 #else
16365 {
16366 MA_ATOMIC_LOAD_EXPLICIT_LOCK(16, dst, order);
16367 }
16368 #endif
16369 }
16370 static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* dst, ma_atomic_memory_order order)
16371 {
16372 #if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64))
16373 {
16374 ma_uint32 result;
16375 #if defined(MA_X86) || defined(MA_X64)
16376 {
16377 if (order == ma_atomic_memory_order_relaxed) {
16378 MA_ATOMIC_LOAD_RELAXED_GCC_X86("l", result, dst);
16379 } else if (order <= ma_atomic_memory_order_release) {
16380 MA_ATOMIC_LOAD_RELEASE_GCC_X86("l", result, dst);
16381 } else {
16382 MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("l", result, dst);
16383 }
16384 }
16385 #else
16386 {
16387 #error Unsupported architecture.
16388 }
16389 #endif
16390 return result;
16391 }
16392 #else
16393 {
16394 MA_ATOMIC_LOAD_EXPLICIT_LOCK(32, dst, order);
16395 }
16396 #endif
16397 }
16398 static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* dst, ma_atomic_memory_order order)
16399 {
16400 #if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64))
16401 {
16402 ma_uint64 result;
16403 #if defined(MA_X64)
16404 {
16405 if (order == ma_atomic_memory_order_relaxed) {
16406 MA_ATOMIC_LOAD_RELAXED_GCC_X86("q", result, dst);
16407 } else if (order <= ma_atomic_memory_order_release) {
16408 MA_ATOMIC_LOAD_RELEASE_GCC_X86("q", result, dst);
16409 } else {
16410 MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("q", result, dst);
16411 }
16412 }
16413 #elif defined(MA_X86)
16414 {
16415 (void)order;
16416 return ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, 0, 0);
16417 }
16418 #else
16419 {
16420 #error Unsupported architecture.
16421 }
16422 #endif
16423 return result;
16424 }
16425 #else
16426 {
16427 MA_ATOMIC_LOAD_EXPLICIT_LOCK(64, dst, order);
16428 }
16429 #endif
16430 }
16431 static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
16432 {
16433 #if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64))
16434 {
16435 ma_uint8 result;
16436 (void)order;
16437 #if defined(MA_X86) || defined(MA_X64)
16438 {
16439 MA_ATOMIC_XCHG_GCC_X86("b", result, dst, src);
16440 }
16441 #else
16442 {
16443 #error Unsupported architecture.
16444 }
16445 #endif
16446 return result;
16447 }
16448 #else
16449 {
16450 MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(8, dst, src, order);
16451 }
16452 #endif
16453 }
16454 static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
16455 {
16456 #if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64))
16457 {
16458 ma_uint16 result;
16459 (void)order;
16460 #if defined(MA_X86) || defined(MA_X64)
16461 {
16462 MA_ATOMIC_XCHG_GCC_X86("w", result, dst, src);
16463 }
16464 #else
16465 {
16466 #error Unsupported architecture.
16467 }
16468 #endif
16469 return result;
16470 }
16471 #else
16472 {
16473 MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(16, dst, src, order);
16474 }
16475 #endif
16476 }
16477 static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
16478 {
16479 #if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64))
16480 {
16481 ma_uint32 result;
16482 (void)order;
16483 #if defined(MA_X86) || defined(MA_X64)
16484 {
16485 MA_ATOMIC_XCHG_GCC_X86("l", result, dst, src);
16486 }
16487 #else
16488 {
16489 #error Unsupported architecture.
16490 }
16491 #endif
16492 return result;
16493 }
16494 #else
16495 {
16496 MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(32, dst, src, order);
16497 }
16498 #endif
16499 }
16500 static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
16501 {
16502 #if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64))
16503 {
16504 ma_uint64 result;
16505 (void)order;
16506 #if defined(MA_X86)
16507 {
16508 MA_ATOMIC_EXCHANGE_EXPLICIT_CAS(64, dst, src, order);
16509 }
16510 #elif defined(MA_X64)
16511 {
16512 MA_ATOMIC_XCHG_GCC_X86("q", result, dst, src);
16513 }
16514 #else
16515 {
16516 #error Unsupported architecture.
16517 }
16518 #endif
16519 return result;
16520 }
16521 #else
16522 {
16523 MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(64, dst, src, order);
16524 }
16525 #endif
16526 }
16527 static MA_INLINE void ma_atomic_store_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
16528 {
16529 #if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64))
16530 {
16531 #if defined(MA_X86) || defined(MA_X64)
16532 {
16533 if (order == ma_atomic_memory_order_relaxed) {
16534 __asm__ __volatile__ (
16535 "movb %1, %0"
16536 : "=m"(*dst)
16537 : "r"(src)
16538 );
16539 } else {
16540 __asm__ __volatile__ (
16541 "xchgb %1, %0"
16542 : "=m"(*dst)
16543 : "r"(src)
16544 : "memory"
16545 );
16546 }
16547 }
16548 #else
16549 {
16550 #error Unsupported architecture.
16551 }
16552 #endif
16553 }
16554 #else
16555 {
16556 MA_ATOMIC_STORE_EXPLICIT_LOCK(8, dst, src, order);
16557 }
16558 #endif
16559 }
16560 static MA_INLINE void ma_atomic_store_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
16561 {
16562 #if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64))
16563 {
16564 #if defined(MA_X86) || defined(MA_X64)
16565 {
16566 if (order == ma_atomic_memory_order_relaxed) {
16567 __asm__ __volatile__ (
16568 "movw %1, %0"
16569 : "=m"(*dst)
16570 : "r"(src)
16571 );
16572 } else {
16573 __asm__ __volatile__ (
16574 "xchgw %1, %0"
16575 : "=m"(*dst)
16576 : "r"(src)
16577 : "memory"
16578 );
16579 }
16580 }
16581 #else
16582 {
16583 #error Unsupported architecture.
16584 }
16585 #endif
16586 }
16587 #else
16588 {
16589 MA_ATOMIC_STORE_EXPLICIT_LOCK(16, dst, src, order);
16590 }
16591 #endif
16592 }
16593 static MA_INLINE void ma_atomic_store_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
16594 {
16595 #if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64))
16596 {
16597 #if defined(MA_X86) || defined(MA_X64)
16598 {
16599 if (order == ma_atomic_memory_order_relaxed) {
16600 __asm__ __volatile__ (
16601 "movl %1, %0"
16602 : "=m"(*dst)
16603 : "r"(src)
16604 );
16605 } else {
16606 __asm__ __volatile__ (
16607 "xchgl %1, %0"
16608 : "=m"(*dst)
16609 : "r"(src)
16610 : "memory"
16611 );
16612 }
16613 }
16614 #else
16615 {
16616 #error Unsupported architecture.
16617 }
16618 #endif
16619 }
16620 #else
16621 {
16622 MA_ATOMIC_STORE_EXPLICIT_LOCK(32, dst, src, order);
16623 }
16624 #endif
16625 }
16626 static MA_INLINE void ma_atomic_store_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
16627 {
16628 #if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64))
16629 {
16630 #if defined(MA_X64)
16631 {
16632 if (order == ma_atomic_memory_order_relaxed) {
16633 __asm__ __volatile__ (
16634 "movq %1, %0"
16635 : "=m"(*dst)
16636 : "r"(src)
16637 );
16638 } else {
16639 __asm__ __volatile__ (
16640 "xchgq %1, %0"
16641 : "=m"(*dst)
16642 : "r"(src)
16643 : "memory"
16644 );
16645 }
16646 }
16647 #else
16648 {
16649 MA_ATOMIC_STORE_EXPLICIT_CAS(64, dst, src, order);
16650 }
16651 #endif
16652 }
16653 #else
16654 {
16655 MA_ATOMIC_STORE_EXPLICIT_LOCK(64, dst, src, order);
16656 }
16657 #endif
16658 }
16659 static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
16660 {
16661 #if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64))
16662 {
16663 #if defined(MA_X86) || defined(MA_X64)
16664 {
16665 ma_uint8 result;
16666 (void)order;
16667 MA_ATOMIC_XADD_GCC_X86("b", result, dst, src);
16668 return result;
16669 }
16670 #else
16671 {
16672 #error Unsupported architecture.
16673 }
16674 #endif
16675 }
16676 #else
16677 {
16678 MA_ATOMIC_FETCH_ADD_LOCK(8, dst, src, order);
16679 }
16680 #endif
16681 }
16682 static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
16683 {
16684 #if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64))
16685 {
16686 #if defined(MA_X86) || defined(MA_X64)
16687 {
16688 ma_uint16 result;
16689 (void)order;
16690 MA_ATOMIC_XADD_GCC_X86("w", result, dst, src);
16691 return result;
16692 }
16693 #else
16694 {
16695 #error Unsupported architecture.
16696 }
16697 #endif
16698 }
16699 #else
16700 {
16701 MA_ATOMIC_FETCH_ADD_LOCK(16, dst, src, order);
16702 }
16703 #endif
16704 }
16705 static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
16706 {
16707 #if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64))
16708 {
16709 #if defined(MA_X86) || defined(MA_X64)
16710 {
16711 ma_uint32 result;
16712 (void)order;
16713 MA_ATOMIC_XADD_GCC_X86("l", result, dst, src);
16714 return result;
16715 }
16716 #else
16717 {
16718 #error Unsupported architecture.
16719 }
16720 #endif
16721 }
16722 #else
16723 {
16724 MA_ATOMIC_FETCH_ADD_LOCK(32, dst, src, order);
16725 }
16726 #endif
16727 }
16728 static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
16729 {
16730 #if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64))
16731 {
16732 #if defined(MA_X86)
16733 {
16734 MA_ATOMIC_FETCH_ADD_CAS(64, dst, src, order);
16735 }
16736 #elif defined(MA_X64)
16737 {
16738 ma_uint64 result;
16739 MA_ATOMIC_XADD_GCC_X86("q", result, dst, src);
16740 (void)order;
16741 return result;
16742 }
16743 #else
16744 {
16745 #error Unsupported architecture.
16746 }
16747 #endif
16748 }
16749 #else
16750 {
16751 MA_ATOMIC_FETCH_ADD_LOCK(64, dst, src, order);
16752 }
16753 #endif
16754 }
16755 static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
16756 {
16757 return ma_atomic_fetch_add_explicit_8(dst, (ma_uint8)(-(ma_int8)src), order);
16758 }
16759 static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
16760 {
16761 return ma_atomic_fetch_add_explicit_16(dst, (ma_uint16)(-(ma_int16)src), order);
16762 }
16763 static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
16764 {
16765 return ma_atomic_fetch_add_explicit_32(dst, (ma_uint32)(-(ma_int32)src), order);
16766 }
16767 static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
16768 {
16769 return ma_atomic_fetch_add_explicit_64(dst, (ma_uint64)(-(ma_int64)src), order);
16770 }
16771 static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
16772 {
16773 MA_ATOMIC_FETCH_AND_CAS(8, dst, src, order);
16774 }
16775 static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
16776 {
16777 MA_ATOMIC_FETCH_AND_CAS(16, dst, src, order);
16778 }
16779 static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
16780 {
16781 MA_ATOMIC_FETCH_AND_CAS(32, dst, src, order);
16782 }
16783 static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
16784 {
16785 MA_ATOMIC_FETCH_AND_CAS(64, dst, src, order);
16786 }
16787 static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
16788 {
16789 MA_ATOMIC_FETCH_OR_CAS(8, dst, src, order);
16790 }
16791 static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
16792 {
16793 MA_ATOMIC_FETCH_OR_CAS(16, dst, src, order);
16794 }
16795 static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
16796 {
16797 MA_ATOMIC_FETCH_OR_CAS(32, dst, src, order);
16798 }
16799 static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
16800 {
16801 MA_ATOMIC_FETCH_OR_CAS(64, dst, src, order);
16802 }
16803 static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
16804 {
16805 MA_ATOMIC_FETCH_XOR_CAS(8, dst, src, order);
16806 }
16807 static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
16808 {
16809 MA_ATOMIC_FETCH_XOR_CAS(16, dst, src, order);
16810 }
16811 static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
16812 {
16813 MA_ATOMIC_FETCH_XOR_CAS(32, dst, src, order);
16814 }
16815 static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
16816 {
16817 MA_ATOMIC_FETCH_XOR_CAS(64, dst, src, order);
16818 }
16819 #else
16820 #error Unsupported compiler.
16821 #endif
16822#endif
16823#if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE)
16824 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
16825 {
16826 ma_uint8 result;
16827 (void)successOrder;
16828 (void)failureOrder;
16829 result = ma_atomic_compare_and_swap_8(dst, *expected, replacement);
16830 if (result == *expected) {
16831 return 1;
16832 } else {
16833 *expected = result;
16834 return 0;
16835 }
16836 }
16837 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
16838 {
16839 ma_uint16 result;
16840 (void)successOrder;
16841 (void)failureOrder;
16842 result = ma_atomic_compare_and_swap_16(dst, *expected, replacement);
16843 if (result == *expected) {
16844 return 1;
16845 } else {
16846 *expected = result;
16847 return 0;
16848 }
16849 }
16850 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
16851 {
16852 ma_uint32 result;
16853 (void)successOrder;
16854 (void)failureOrder;
16855 result = ma_atomic_compare_and_swap_32(dst, *expected, replacement);
16856 if (result == *expected) {
16857 return 1;
16858 } else {
16859 *expected = result;
16860 return 0;
16861 }
16862 }
16863 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
16864 {
16865 ma_uint64 result;
16866 (void)successOrder;
16867 (void)failureOrder;
16868 result = ma_atomic_compare_and_swap_64(dst, *expected, replacement);
16869 if (result == *expected) {
16870 return 1;
16871 } else {
16872 *expected = result;
16873 return 0;
16874 }
16875 }
16876 #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, replacement, successOrder, failureOrder)
16877 #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, replacement, successOrder, failureOrder)
16878 #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, replacement, successOrder, failureOrder)
16879 #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, replacement, successOrder, failureOrder)
16880#endif
16881#if defined(MA_64BIT)
16882 static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr)
16883 {
16884 return ma_atomic_is_lock_free_64((volatile ma_uint64*)ptr);
16885 }
16886 static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order)
16887 {
16888 return (void*)ma_atomic_load_explicit_64((volatile ma_uint64*)ptr, order);
16889 }
16890 static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order)
16891 {
16892 ma_atomic_store_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order);
16893 }
16894 static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order)
16895 {
16896 return (void*)ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order);
16897 }
16898 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
16899 {
16900 return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)replacement, successOrder, failureOrder);
16901 }
16902 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
16903 {
16904 return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)replacement, successOrder, failureOrder);
16905 }
16906 static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* replacement)
16907 {
16908 return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)replacement);
16909 }
16910#elif defined(MA_32BIT)
16911 static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr)
16912 {
16913 return ma_atomic_is_lock_free_32((volatile ma_uint32*)ptr);
16914 }
16915 static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order)
16916 {
16917 return (void*)ma_atomic_load_explicit_32((volatile ma_uint32*)ptr, order);
16918 }
16919 static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order)
16920 {
16921 ma_atomic_store_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order);
16922 }
16923 static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order)
16924 {
16925 return (void*)ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order);
16926 }
16927 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
16928 {
16929 return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)replacement, successOrder, failureOrder);
16930 }
16931 static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
16932 {
16933 return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)replacement, successOrder, failureOrder);
16934 }
16935 static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* replacement)
16936 {
16937 return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)replacement);
16938 }
16939#else
16940 #error Unsupported architecture.
16941#endif
16942#define ma_atomic_store_ptr(dst, src) ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst)
16943#define ma_atomic_load_ptr(ptr) ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst)
16944#define ma_atomic_exchange_ptr(dst, src) ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst)
16945#define ma_atomic_compare_exchange_strong_ptr(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
16946#define ma_atomic_compare_exchange_weak_ptr(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
16947#define ma_atomic_store_8( dst, src) ma_atomic_store_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)
16948#define ma_atomic_store_16(dst, src) ma_atomic_store_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)
16949#define ma_atomic_store_32(dst, src) ma_atomic_store_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)
16950#define ma_atomic_store_64(dst, src) ma_atomic_store_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)
16951#define ma_atomic_load_8( ptr) ma_atomic_load_explicit_8( ptr, ma_atomic_memory_order_seq_cst)
16952#define ma_atomic_load_16(ptr) ma_atomic_load_explicit_16(ptr, ma_atomic_memory_order_seq_cst)
16953#define ma_atomic_load_32(ptr) ma_atomic_load_explicit_32(ptr, ma_atomic_memory_order_seq_cst)
16954#define ma_atomic_load_64(ptr) ma_atomic_load_explicit_64(ptr, ma_atomic_memory_order_seq_cst)
16955#define ma_atomic_exchange_8( dst, src) ma_atomic_exchange_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)
16956#define ma_atomic_exchange_16(dst, src) ma_atomic_exchange_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)
16957#define ma_atomic_exchange_32(dst, src) ma_atomic_exchange_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)
16958#define ma_atomic_exchange_64(dst, src) ma_atomic_exchange_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)
16959#define ma_atomic_compare_exchange_strong_8( dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_8( dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
16960#define ma_atomic_compare_exchange_strong_16(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
16961#define ma_atomic_compare_exchange_strong_32(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
16962#define ma_atomic_compare_exchange_strong_64(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
16963#define ma_atomic_compare_exchange_weak_8( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_8( dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
16964#define ma_atomic_compare_exchange_weak_16( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_16(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
16965#define ma_atomic_compare_exchange_weak_32( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
16966#define ma_atomic_compare_exchange_weak_64( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
16967#define ma_atomic_fetch_add_8( dst, src) ma_atomic_fetch_add_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)
16968#define ma_atomic_fetch_add_16(dst, src) ma_atomic_fetch_add_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)
16969#define ma_atomic_fetch_add_32(dst, src) ma_atomic_fetch_add_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)
16970#define ma_atomic_fetch_add_64(dst, src) ma_atomic_fetch_add_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)
16971#define ma_atomic_fetch_sub_8( dst, src) ma_atomic_fetch_sub_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)
16972#define ma_atomic_fetch_sub_16(dst, src) ma_atomic_fetch_sub_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)
16973#define ma_atomic_fetch_sub_32(dst, src) ma_atomic_fetch_sub_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)
16974#define ma_atomic_fetch_sub_64(dst, src) ma_atomic_fetch_sub_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)
16975#define ma_atomic_fetch_or_8( dst, src) ma_atomic_fetch_or_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)
16976#define ma_atomic_fetch_or_16(dst, src) ma_atomic_fetch_or_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)
16977#define ma_atomic_fetch_or_32(dst, src) ma_atomic_fetch_or_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)
16978#define ma_atomic_fetch_or_64(dst, src) ma_atomic_fetch_or_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)
16979#define ma_atomic_fetch_xor_8( dst, src) ma_atomic_fetch_xor_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)
16980#define ma_atomic_fetch_xor_16(dst, src) ma_atomic_fetch_xor_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)
16981#define ma_atomic_fetch_xor_32(dst, src) ma_atomic_fetch_xor_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)
16982#define ma_atomic_fetch_xor_64(dst, src) ma_atomic_fetch_xor_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)
16983#define ma_atomic_fetch_and_8( dst, src) ma_atomic_fetch_and_explicit_8 (dst, src, ma_atomic_memory_order_seq_cst)
16984#define ma_atomic_fetch_and_16(dst, src) ma_atomic_fetch_and_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)
16985#define ma_atomic_fetch_and_32(dst, src) ma_atomic_fetch_and_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)
16986#define ma_atomic_fetch_and_64(dst, src) ma_atomic_fetch_and_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)
16987#define ma_atomic_store_explicit_i8( dst, src, order) ma_atomic_store_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)
16988#define ma_atomic_store_explicit_i16(dst, src, order) ma_atomic_store_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)
16989#define ma_atomic_store_explicit_i32(dst, src, order) ma_atomic_store_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)
16990#define ma_atomic_store_explicit_i64(dst, src, order) ma_atomic_store_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)
16991#define ma_atomic_load_explicit_i8( ptr, order) (ma_int8 )ma_atomic_load_explicit_8( (ma_uint8* )ptr, order)
16992#define ma_atomic_load_explicit_i16(ptr, order) (ma_int16)ma_atomic_load_explicit_16((ma_uint16*)ptr, order)
16993#define ma_atomic_load_explicit_i32(ptr, order) (ma_int32)ma_atomic_load_explicit_32((ma_uint32*)ptr, order)
16994#define ma_atomic_load_explicit_i64(ptr, order) (ma_int64)ma_atomic_load_explicit_64((ma_uint64*)ptr, order)
16995#define ma_atomic_exchange_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_exchange_explicit_8 ((ma_uint8* )dst, (ma_uint8 )src, order)
16996#define ma_atomic_exchange_explicit_i16(dst, src, order) (ma_int16)ma_atomic_exchange_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)
16997#define ma_atomic_exchange_explicit_i32(dst, src, order) (ma_int32)ma_atomic_exchange_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)
16998#define ma_atomic_exchange_explicit_i64(dst, src, order) (ma_int64)ma_atomic_exchange_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)
16999#define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )replacement, successOrder, failureOrder)
17000#define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)replacement, successOrder, failureOrder)
17001#define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)replacement, successOrder, failureOrder)
17002#define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)replacement, successOrder, failureOrder)
17003#define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )replacement, successOrder, failureOrder)
17004#define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)replacement, successOrder, failureOrder)
17005#define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)replacement, successOrder, failureOrder)
17006#define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)replacement, successOrder, failureOrder)
17007#define ma_atomic_fetch_add_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_add_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)
17008#define ma_atomic_fetch_add_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_add_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)
17009#define ma_atomic_fetch_add_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_add_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)
17010#define ma_atomic_fetch_add_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_add_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)
17011#define ma_atomic_fetch_sub_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_sub_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)
17012#define ma_atomic_fetch_sub_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_sub_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)
17013#define ma_atomic_fetch_sub_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_sub_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)
17014#define ma_atomic_fetch_sub_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_sub_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)
17015#define ma_atomic_fetch_or_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_or_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)
17016#define ma_atomic_fetch_or_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_or_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)
17017#define ma_atomic_fetch_or_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_or_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)
17018#define ma_atomic_fetch_or_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_or_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)
17019#define ma_atomic_fetch_xor_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_xor_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)
17020#define ma_atomic_fetch_xor_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_xor_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)
17021#define ma_atomic_fetch_xor_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_xor_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)
17022#define ma_atomic_fetch_xor_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_xor_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)
17023#define ma_atomic_fetch_and_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_and_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)
17024#define ma_atomic_fetch_and_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_and_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)
17025#define ma_atomic_fetch_and_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_and_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)
17026#define ma_atomic_fetch_and_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_and_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)
17027#define ma_atomic_store_i8( dst, src) ma_atomic_store_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)
17028#define ma_atomic_store_i16(dst, src) ma_atomic_store_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)
17029#define ma_atomic_store_i32(dst, src) ma_atomic_store_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)
17030#define ma_atomic_store_i64(dst, src) ma_atomic_store_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)
17031#define ma_atomic_load_i8( ptr) ma_atomic_load_explicit_i8( ptr, ma_atomic_memory_order_seq_cst)
17032#define ma_atomic_load_i16(ptr) ma_atomic_load_explicit_i16(ptr, ma_atomic_memory_order_seq_cst)
17033#define ma_atomic_load_i32(ptr) ma_atomic_load_explicit_i32(ptr, ma_atomic_memory_order_seq_cst)
17034#define ma_atomic_load_i64(ptr) ma_atomic_load_explicit_i64(ptr, ma_atomic_memory_order_seq_cst)
17035#define ma_atomic_exchange_i8( dst, src) ma_atomic_exchange_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)
17036#define ma_atomic_exchange_i16(dst, src) ma_atomic_exchange_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)
17037#define ma_atomic_exchange_i32(dst, src) ma_atomic_exchange_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)
17038#define ma_atomic_exchange_i64(dst, src) ma_atomic_exchange_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)
17039#define ma_atomic_compare_exchange_strong_i8( dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
17040#define ma_atomic_compare_exchange_strong_i16(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
17041#define ma_atomic_compare_exchange_strong_i32(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
17042#define ma_atomic_compare_exchange_strong_i64(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
17043#define ma_atomic_compare_exchange_weak_i8( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
17044#define ma_atomic_compare_exchange_weak_i16(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
17045#define ma_atomic_compare_exchange_weak_i32(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
17046#define ma_atomic_compare_exchange_weak_i64(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
17047#define ma_atomic_fetch_add_i8( dst, src) ma_atomic_fetch_add_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)
17048#define ma_atomic_fetch_add_i16(dst, src) ma_atomic_fetch_add_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)
17049#define ma_atomic_fetch_add_i32(dst, src) ma_atomic_fetch_add_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)
17050#define ma_atomic_fetch_add_i64(dst, src) ma_atomic_fetch_add_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)
17051#define ma_atomic_fetch_sub_i8( dst, src) ma_atomic_fetch_sub_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)
17052#define ma_atomic_fetch_sub_i16(dst, src) ma_atomic_fetch_sub_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)
17053#define ma_atomic_fetch_sub_i32(dst, src) ma_atomic_fetch_sub_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)
17054#define ma_atomic_fetch_sub_i64(dst, src) ma_atomic_fetch_sub_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)
17055#define ma_atomic_fetch_or_i8( dst, src) ma_atomic_fetch_or_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)
17056#define ma_atomic_fetch_or_i16(dst, src) ma_atomic_fetch_or_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)
17057#define ma_atomic_fetch_or_i32(dst, src) ma_atomic_fetch_or_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)
17058#define ma_atomic_fetch_or_i64(dst, src) ma_atomic_fetch_or_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)
17059#define ma_atomic_fetch_xor_i8( dst, src) ma_atomic_fetch_xor_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)
17060#define ma_atomic_fetch_xor_i16(dst, src) ma_atomic_fetch_xor_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)
17061#define ma_atomic_fetch_xor_i32(dst, src) ma_atomic_fetch_xor_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)
17062#define ma_atomic_fetch_xor_i64(dst, src) ma_atomic_fetch_xor_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)
17063#define ma_atomic_fetch_and_i8( dst, src) ma_atomic_fetch_and_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)
17064#define ma_atomic_fetch_and_i16(dst, src) ma_atomic_fetch_and_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)
17065#define ma_atomic_fetch_and_i32(dst, src) ma_atomic_fetch_and_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)
17066#define ma_atomic_fetch_and_i64(dst, src) ma_atomic_fetch_and_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)
17067#define ma_atomic_compare_and_swap_i8( dst, expected, dedsired) (ma_int8 )ma_atomic_compare_and_swap_8( (ma_uint8* )dst, (ma_uint8 )expected, (ma_uint8 )dedsired)
17068#define ma_atomic_compare_and_swap_i16(dst, expected, dedsired) (ma_int16)ma_atomic_compare_and_swap_16((ma_uint16*)dst, (ma_uint16)expected, (ma_uint16)dedsired)
17069#define ma_atomic_compare_and_swap_i32(dst, expected, dedsired) (ma_int32)ma_atomic_compare_and_swap_32((ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)dedsired)
17070#define ma_atomic_compare_and_swap_i64(dst, expected, dedsired) (ma_int64)ma_atomic_compare_and_swap_64((ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)dedsired)
17071typedef union
17072{
17073 ma_uint32 i;
17074 float f;
17075} ma_atomic_if32;
17076typedef union
17077{
17078 ma_uint64 i;
17079 double f;
17080} ma_atomic_if64;
17081#define ma_atomic_clear_explicit_f32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order)
17082#define ma_atomic_clear_explicit_f64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order)
17083static MA_INLINE void ma_atomic_store_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)
17084{
17085 ma_atomic_if32 x;
17086 x.f = src;
17087 ma_atomic_store_explicit_32((volatile ma_uint32*)dst, x.i, order);
17088}
17089static MA_INLINE void ma_atomic_store_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)
17090{
17091 ma_atomic_if64 x;
17092 x.f = src;
17093 ma_atomic_store_explicit_64((volatile ma_uint64*)dst, x.i, order);
17094}
17095static MA_INLINE float ma_atomic_load_explicit_f32(volatile const float* ptr, ma_atomic_memory_order order)
17096{
17097 ma_atomic_if32 r;
17098 r.i = ma_atomic_load_explicit_32((volatile const ma_uint32*)ptr, order);
17099 return r.f;
17100}
17101static MA_INLINE double ma_atomic_load_explicit_f64(volatile const double* ptr, ma_atomic_memory_order order)
17102{
17103 ma_atomic_if64 r;
17104 r.i = ma_atomic_load_explicit_64((volatile const ma_uint64*)ptr, order);
17105 return r.f;
17106}
17107static MA_INLINE float ma_atomic_exchange_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)
17108{
17109 ma_atomic_if32 r;
17110 ma_atomic_if32 x;
17111 x.f = src;
17112 r.i = ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, x.i, order);
17113 return r.f;
17114}
17115static MA_INLINE double ma_atomic_exchange_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)
17116{
17117 ma_atomic_if64 r;
17118 ma_atomic_if64 x;
17119 x.f = src;
17120 r.i = ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, x.i, order);
17121 return r.f;
17122}
17123static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
17124{
17125 ma_atomic_if32 d;
17126 d.f = replacement;
17127 return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder);
17128}
17129static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
17130{
17131 ma_atomic_if64 d;
17132 d.f = replacement;
17133 return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder);
17134}
17135static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
17136{
17137 ma_atomic_if32 d;
17138 d.f = replacement;
17139 return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder);
17140}
17141static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
17142{
17143 ma_atomic_if64 d;
17144 d.f = replacement;
17145 return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder);
17146}
17147static MA_INLINE float ma_atomic_fetch_add_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)
17148{
17149 ma_atomic_if32 r;
17150 ma_atomic_if32 x;
17151 x.f = src;
17152 r.i = ma_atomic_fetch_add_explicit_32((volatile ma_uint32*)dst, x.i, order);
17153 return r.f;
17154}
17155static MA_INLINE double ma_atomic_fetch_add_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)
17156{
17157 ma_atomic_if64 r;
17158 ma_atomic_if64 x;
17159 x.f = src;
17160 r.i = ma_atomic_fetch_add_explicit_64((volatile ma_uint64*)dst, x.i, order);
17161 return r.f;
17162}
17163static MA_INLINE float ma_atomic_fetch_sub_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)
17164{
17165 ma_atomic_if32 r;
17166 ma_atomic_if32 x;
17167 x.f = src;
17168 r.i = ma_atomic_fetch_sub_explicit_32((volatile ma_uint32*)dst, x.i, order);
17169 return r.f;
17170}
17171static MA_INLINE double ma_atomic_fetch_sub_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)
17172{
17173 ma_atomic_if64 r;
17174 ma_atomic_if64 x;
17175 x.f = src;
17176 r.i = ma_atomic_fetch_sub_explicit_64((volatile ma_uint64*)dst, x.i, order);
17177 return r.f;
17178}
17179static MA_INLINE float ma_atomic_fetch_or_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)
17180{
17181 ma_atomic_if32 r;
17182 ma_atomic_if32 x;
17183 x.f = src;
17184 r.i = ma_atomic_fetch_or_explicit_32((volatile ma_uint32*)dst, x.i, order);
17185 return r.f;
17186}
17187static MA_INLINE double ma_atomic_fetch_or_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)
17188{
17189 ma_atomic_if64 r;
17190 ma_atomic_if64 x;
17191 x.f = src;
17192 r.i = ma_atomic_fetch_or_explicit_64((volatile ma_uint64*)dst, x.i, order);
17193 return r.f;
17194}
17195static MA_INLINE float ma_atomic_fetch_xor_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)
17196{
17197 ma_atomic_if32 r;
17198 ma_atomic_if32 x;
17199 x.f = src;
17200 r.i = ma_atomic_fetch_xor_explicit_32((volatile ma_uint32*)dst, x.i, order);
17201 return r.f;
17202}
17203static MA_INLINE double ma_atomic_fetch_xor_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)
17204{
17205 ma_atomic_if64 r;
17206 ma_atomic_if64 x;
17207 x.f = src;
17208 r.i = ma_atomic_fetch_xor_explicit_64((volatile ma_uint64*)dst, x.i, order);
17209 return r.f;
17210}
17211static MA_INLINE float ma_atomic_fetch_and_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)
17212{
17213 ma_atomic_if32 r;
17214 ma_atomic_if32 x;
17215 x.f = src;
17216 r.i = ma_atomic_fetch_and_explicit_32((volatile ma_uint32*)dst, x.i, order);
17217 return r.f;
17218}
17219static MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)
17220{
17221 ma_atomic_if64 r;
17222 ma_atomic_if64 x;
17223 x.f = src;
17224 r.i = ma_atomic_fetch_and_explicit_64((volatile ma_uint64*)dst, x.i, order);
17225 return r.f;
17226}
17227#define ma_atomic_clear_f32(ptr) (float )ma_atomic_clear_explicit_f32(ptr, ma_atomic_memory_order_seq_cst)
17228#define ma_atomic_clear_f64(ptr) (double)ma_atomic_clear_explicit_f64(ptr, ma_atomic_memory_order_seq_cst)
17229#define ma_atomic_store_f32(dst, src) ma_atomic_store_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)
17230#define ma_atomic_store_f64(dst, src) ma_atomic_store_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)
17231#define ma_atomic_load_f32(ptr) (float )ma_atomic_load_explicit_f32(ptr, ma_atomic_memory_order_seq_cst)
17232#define ma_atomic_load_f64(ptr) (double)ma_atomic_load_explicit_f64(ptr, ma_atomic_memory_order_seq_cst)
17233#define ma_atomic_exchange_f32(dst, src) (float )ma_atomic_exchange_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)
17234#define ma_atomic_exchange_f64(dst, src) (double)ma_atomic_exchange_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)
17235#define ma_atomic_compare_exchange_strong_f32(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
17236#define ma_atomic_compare_exchange_strong_f64(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
17237#define ma_atomic_compare_exchange_weak_f32(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
17238#define ma_atomic_compare_exchange_weak_f64(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
17239#define ma_atomic_fetch_add_f32(dst, src) ma_atomic_fetch_add_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)
17240#define ma_atomic_fetch_add_f64(dst, src) ma_atomic_fetch_add_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)
17241#define ma_atomic_fetch_sub_f32(dst, src) ma_atomic_fetch_sub_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)
17242#define ma_atomic_fetch_sub_f64(dst, src) ma_atomic_fetch_sub_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)
17243#define ma_atomic_fetch_or_f32(dst, src) ma_atomic_fetch_or_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)
17244#define ma_atomic_fetch_or_f64(dst, src) ma_atomic_fetch_or_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)
17245#define ma_atomic_fetch_xor_f32(dst, src) ma_atomic_fetch_xor_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)
17246#define ma_atomic_fetch_xor_f64(dst, src) ma_atomic_fetch_xor_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)
17247#define ma_atomic_fetch_and_f32(dst, src) ma_atomic_fetch_and_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)
17248#define ma_atomic_fetch_and_f64(dst, src) ma_atomic_fetch_and_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)
17249static MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float replacement)
17250{
17251 ma_atomic_if32 r;
17252 ma_atomic_if32 e, d;
17253 e.f = expected;
17254 d.f = replacement;
17255 r.i = ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, e.i, d.i);
17256 return r.f;
17257}
17258static MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double replacement)
17259{
17260 ma_atomic_if64 r;
17261 ma_atomic_if64 e, d;
17262 e.f = expected;
17263 d.f = replacement;
17264 r.i = ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, e.i, d.i);
17265 return r.f;
17266}
17267#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
17268 #pragma GCC diagnostic pop
17269#endif
17270#if defined(__cplusplus)
17271}
17272#endif
17273#endif
17274/* c89atomic.h end */
17275
17276#define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \
17277 static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \
17278 { \
17279 return (ma_##type)ma_atomic_load_##c89TypeExtension(&x->value); \
17280 } \
17281 static MA_INLINE void ma_atomic_##type##_set(ma_atomic_##type* x, ma_##type value) \
17282 { \
17283 ma_atomic_store_##c89TypeExtension(&x->value, value); \
17284 } \
17285 static MA_INLINE ma_##type ma_atomic_##type##_exchange(ma_atomic_##type* x, ma_##type value) \
17286 { \
17287 return (ma_##type)ma_atomic_exchange_##c89TypeExtension(&x->value, value); \
17288 } \
17289 static MA_INLINE ma_bool32 ma_atomic_##type##_compare_exchange(ma_atomic_##type* x, ma_##type* expected, ma_##type desired) \
17290 { \
17291 return ma_atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \
17292 } \
17293 static MA_INLINE ma_##type ma_atomic_##type##_fetch_add(ma_atomic_##type* x, ma_##type y) \
17294 { \
17295 return (ma_##type)ma_atomic_fetch_add_##c89TypeExtension(&x->value, y); \
17296 } \
17297 static MA_INLINE ma_##type ma_atomic_##type##_fetch_sub(ma_atomic_##type* x, ma_##type y) \
17298 { \
17299 return (ma_##type)ma_atomic_fetch_sub_##c89TypeExtension(&x->value, y); \
17300 } \
17301 static MA_INLINE ma_##type ma_atomic_##type##_fetch_or(ma_atomic_##type* x, ma_##type y) \
17302 { \
17303 return (ma_##type)ma_atomic_fetch_or_##c89TypeExtension(&x->value, y); \
17304 } \
17305 static MA_INLINE ma_##type ma_atomic_##type##_fetch_xor(ma_atomic_##type* x, ma_##type y) \
17306 { \
17307 return (ma_##type)ma_atomic_fetch_xor_##c89TypeExtension(&x->value, y); \
17308 } \
17309 static MA_INLINE ma_##type ma_atomic_##type##_fetch_and(ma_atomic_##type* x, ma_##type y) \
17310 { \
17311 return (ma_##type)ma_atomic_fetch_and_##c89TypeExtension(&x->value, y); \
17312 } \
17313 static MA_INLINE ma_##type ma_atomic_##type##_compare_and_swap(ma_atomic_##type* x, ma_##type expected, ma_##type desired) \
17314 { \
17315 return (ma_##type)ma_atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \
17316 } \
17317
17318#define MA_ATOMIC_SAFE_TYPE_IMPL_PTR(type) \
17319 static MA_INLINE ma_##type* ma_atomic_ptr_##type##_get(ma_atomic_ptr_##type* x) \
17320 { \
17321 return ma_atomic_load_ptr((void**)&x->value); \
17322 } \
17323 static MA_INLINE void ma_atomic_ptr_##type##_set(ma_atomic_ptr_##type* x, ma_##type* value) \
17324 { \
17325 ma_atomic_store_ptr((void**)&x->value, (void*)value); \
17326 } \
17327 static MA_INLINE ma_##type* ma_atomic_ptr_##type##_exchange(ma_atomic_ptr_##type* x, ma_##type* value) \
17328 { \
17329 return ma_atomic_exchange_ptr((void**)&x->value, (void*)value); \
17330 } \
17331 static MA_INLINE ma_bool32 ma_atomic_ptr_##type##_compare_exchange(ma_atomic_ptr_##type* x, ma_##type** expected, ma_##type* desired) \
17332 { \
17333 return ma_atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \
17334 } \
17335 static MA_INLINE ma_##type* ma_atomic_ptr_##type##_compare_and_swap(ma_atomic_ptr_##type* x, ma_##type* expected, ma_##type* desired) \
17336 { \
17337 return (ma_##type*)ma_atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \
17338 } \
17339
17340MA_ATOMIC_SAFE_TYPE_IMPL(32, uint32)
17341MA_ATOMIC_SAFE_TYPE_IMPL(i32, int32)
17342MA_ATOMIC_SAFE_TYPE_IMPL(64, uint64)
17343MA_ATOMIC_SAFE_TYPE_IMPL(f32, float)
17344MA_ATOMIC_SAFE_TYPE_IMPL(32, bool32)
17345
17346#if !defined(MA_NO_DEVICE_IO)
17347MA_ATOMIC_SAFE_TYPE_IMPL(i32, device_state)
17348#endif
17349
17350
17351MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn)
17352{
17353 /* This is based on the calculation in ma_linear_resampler_get_expected_output_frame_count(). */
17354 ma_uint64 outputFrameCount;
17355 ma_uint64 preliminaryInputFrameCountFromFrac;
17356 ma_uint64 preliminaryInputFrameCount;
17357
17358 if (sampleRateIn == 0 || sampleRateOut == 0 || frameCountIn == 0) {
17359 return 0;
17360 }
17361
17362 if (sampleRateOut == sampleRateIn) {
17363 return frameCountIn;
17364 }
17365
17366 outputFrameCount = (frameCountIn * sampleRateOut) / sampleRateIn;
17367
17368 preliminaryInputFrameCountFromFrac = (outputFrameCount * (sampleRateIn / sampleRateOut)) / sampleRateOut;
17369 preliminaryInputFrameCount = (outputFrameCount * (sampleRateIn % sampleRateOut)) + preliminaryInputFrameCountFromFrac;
17370
17371 if (preliminaryInputFrameCount <= frameCountIn) {
17372 outputFrameCount += 1;
17373 }
17374
17375 return outputFrameCount;
17376}
17377
17378#ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE
17379#define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096
17380#endif
17381
17382
17383
17384#if defined(MA_WIN32)
17385static ma_result ma_result_from_GetLastError(DWORD error)
17386{
17387 switch (error)
17388 {
17389 case ERROR_SUCCESS: return MA_SUCCESS;
17390 case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST;
17391 case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES;
17392 case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY;
17393 case ERROR_DISK_FULL: return MA_NO_SPACE;
17394 case ERROR_HANDLE_EOF: return MA_AT_END;
17395 case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK;
17396 case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS;
17397 case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED;
17398 case ERROR_SEM_TIMEOUT: return MA_TIMEOUT;
17399 case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST;
17400 default: break;
17401 }
17402
17403 return MA_ERROR;
17404}
17405#endif /* MA_WIN32 */
17406
17407
17408/*******************************************************************************
17409
17410Threading
17411
17412*******************************************************************************/
17413static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield)
17414{
17415 if (pSpinlock == NULL) {
17416 return MA_INVALID_ARGS;
17417 }
17418
17419 for (;;) {
17420 if (ma_atomic_exchange_explicit_32(pSpinlock, 1, ma_atomic_memory_order_acquire) == 0) {
17421 break;
17422 }
17423
17424 while (ma_atomic_load_explicit_32(pSpinlock, ma_atomic_memory_order_relaxed) == 1) {
17425 if (yield) {
17426 ma_yield();
17427 }
17428 }
17429 }
17430
17431 return MA_SUCCESS;
17432}
17433
17434MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock)
17435{
17436 return ma_spinlock_lock_ex(pSpinlock, MA_TRUE);
17437}
17438
17440{
17441 return ma_spinlock_lock_ex(pSpinlock, MA_FALSE);
17442}
17443
17445{
17446 if (pSpinlock == NULL) {
17447 return MA_INVALID_ARGS;
17448 }
17449
17450 ma_atomic_store_explicit_32(pSpinlock, 0, ma_atomic_memory_order_release);
17451 return MA_SUCCESS;
17452}
17453
17454
17455#ifndef MA_NO_THREADING
17456#if defined(MA_POSIX)
17457 #define MA_THREADCALL
17458 typedef void* ma_thread_result;
17459#elif defined(MA_WIN32)
17460 #define MA_THREADCALL WINAPI
17461 typedef unsigned long ma_thread_result;
17462#endif
17463
17464typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData);
17465
17466#ifdef MA_POSIX
17467static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)
17468{
17469 int result;
17470 pthread_attr_t* pAttr = NULL;
17471
17472#if !defined(MA_EMSCRIPTEN) && !defined(MA_3DS) && !defined(MA_SWITCH)
17473 /* Try setting the thread priority. It's not critical if anything fails here. */
17474 pthread_attr_t attr;
17475 if (pthread_attr_init(&attr) == 0) {
17476 int scheduler = -1;
17477
17478 /* We successfully initialized our attributes object so we can assign the pointer so it's passed into pthread_create(). */
17479 pAttr = &attr;
17480
17481 /* We need to set the scheduler policy. Only do this if the OS supports pthread_attr_setschedpolicy() */
17482 #if !defined(MA_BEOS)
17483 {
17484 if (priority == ma_thread_priority_idle) {
17485 #ifdef SCHED_IDLE
17486 if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) {
17487 scheduler = SCHED_IDLE;
17488 }
17489 #endif
17490 } else if (priority == ma_thread_priority_realtime) {
17491 #ifdef SCHED_FIFO
17492 if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) {
17493 scheduler = SCHED_FIFO;
17494 }
17495 #endif
17496 #ifdef MA_LINUX
17497 } else {
17498 scheduler = sched_getscheduler(0);
17499 #endif
17500 }
17501 }
17502 #endif
17503
17504 #if defined(_POSIX_THREAD_ATTR_STACKSIZE) && _POSIX_THREAD_ATTR_STACKSIZE >= 0
17505 {
17506 if (stackSize > 0) {
17507 pthread_attr_setstacksize(&attr, stackSize);
17508 }
17509 }
17510 #else
17511 {
17512 (void)stackSize; /* Suppress unused parameter warning. */
17513 }
17514 #endif
17515
17516
17517 if (scheduler != -1) {
17518 int priorityMin = sched_get_priority_min(scheduler);
17519 int priorityMax = sched_get_priority_max(scheduler);
17520 int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */
17521
17522 struct sched_param sched;
17523 if (pthread_attr_getschedparam(&attr, &sched) == 0) {
17524 if (priority == ma_thread_priority_idle) {
17525 sched.sched_priority = priorityMin;
17526 } else if (priority == ma_thread_priority_realtime) {
17527 #if defined(MA_PTHREAD_REALTIME_THREAD_PRIORITY)
17528 {
17529 sched.sched_priority = MA_PTHREAD_REALTIME_THREAD_PRIORITY;
17530 }
17531 #else
17532 {
17533 sched.sched_priority = priorityMax;
17534 }
17535 #endif
17536 } else {
17537 sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */
17538 }
17539
17540 if (sched.sched_priority < priorityMin) {
17541 sched.sched_priority = priorityMin;
17542 }
17543 if (sched.sched_priority > priorityMax) {
17544 sched.sched_priority = priorityMax;
17545 }
17546
17547 /* I'm not treating a failure of setting the priority as a critical error so not aborting on failure here. */
17548 if (pthread_attr_setschedparam(&attr, &sched) == 0) {
17549 #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28)
17550 {
17551 pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
17552 }
17553 #endif
17554 }
17555 }
17556 }
17557 }
17558#else
17559 /* It's the emscripten build. We'll have a few unused parameters. */
17560 (void)priority;
17561 (void)stackSize;
17562#endif
17563
17564 result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData);
17565
17566 /* The thread attributes object is no longer required. */
17567 if (pAttr != NULL) {
17568 pthread_attr_destroy(pAttr);
17569 }
17570
17571 if (result != 0) {
17572 /*
17573 There have been reports that attempting to create a realtime thread can sometimes fail. In this case,
17574 fall back to a normal priority thread.
17575
17576 I'm including a compile-time option here to disable this functionality for those who have a hard
17577 requirement on realtime threads and would rather an explicit failure.
17578 */
17579 #ifndef MA_NO_PTHREAD_REALTIME_PRIORITY_FALLBACK
17580 {
17581 if(result == EPERM && priority == ma_thread_priority_realtime) {
17582 return ma_thread_create__posix(pThread, ma_thread_priority_normal, stackSize, entryProc, pData);
17583 }
17584 }
17585 #endif
17586
17587 return ma_result_from_errno(result);
17588 }
17589
17590 return MA_SUCCESS;
17591}
17592
17593static void ma_thread_wait__posix(ma_thread* pThread)
17594{
17595 pthread_join((pthread_t)*pThread, NULL);
17596}
17597
17598
17599static ma_result ma_mutex_init__posix(ma_mutex* pMutex)
17600{
17601 int result;
17602
17603 if (pMutex == NULL) {
17604 return MA_INVALID_ARGS;
17605 }
17606
17607 MA_ZERO_OBJECT(pMutex);
17608
17609 result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL);
17610 if (result != 0) {
17611 return ma_result_from_errno(result);
17612 }
17613
17614 return MA_SUCCESS;
17615}
17616
17617static void ma_mutex_uninit__posix(ma_mutex* pMutex)
17618{
17619 pthread_mutex_destroy((pthread_mutex_t*)pMutex);
17620}
17621
17622static void ma_mutex_lock__posix(ma_mutex* pMutex)
17623{
17624 pthread_mutex_lock((pthread_mutex_t*)pMutex);
17625}
17626
17627static void ma_mutex_unlock__posix(ma_mutex* pMutex)
17628{
17629 pthread_mutex_unlock((pthread_mutex_t*)pMutex);
17630}
17631
17632
17633static ma_result ma_event_init__posix(ma_event* pEvent)
17634{
17635 int result;
17636
17637 result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL);
17638 if (result != 0) {
17639 return ma_result_from_errno(result);
17640 }
17641
17642 result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL);
17643 if (result != 0) {
17644 pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock);
17645 return ma_result_from_errno(result);
17646 }
17647
17648 pEvent->value = 0;
17649 return MA_SUCCESS;
17650}
17651
17652static void ma_event_uninit__posix(ma_event* pEvent)
17653{
17654 pthread_cond_destroy((pthread_cond_t*)&pEvent->cond);
17655 pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock);
17656}
17657
17658static ma_result ma_event_wait__posix(ma_event* pEvent)
17659{
17660 pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock);
17661 {
17662 while (pEvent->value == 0) {
17663 pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock);
17664 }
17665 pEvent->value = 0; /* Auto-reset. */
17666 }
17667 pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock);
17668
17669 return MA_SUCCESS;
17670}
17671
17672static ma_result ma_event_signal__posix(ma_event* pEvent)
17673{
17674 pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock);
17675 {
17676 pEvent->value = 1;
17677 pthread_cond_signal((pthread_cond_t*)&pEvent->cond);
17678 }
17679 pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock);
17680
17681 return MA_SUCCESS;
17682}
17683
17684
17685static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore)
17686{
17687 int result;
17688
17689 if (pSemaphore == NULL) {
17690 return MA_INVALID_ARGS;
17691 }
17692
17693 pSemaphore->value = initialValue;
17694
17695 result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL);
17696 if (result != 0) {
17697 return ma_result_from_errno(result); /* Failed to create mutex. */
17698 }
17699
17700 result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL);
17701 if (result != 0) {
17702 pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock);
17703 return ma_result_from_errno(result); /* Failed to create condition variable. */
17704 }
17705
17706 return MA_SUCCESS;
17707}
17708
17709static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore)
17710{
17711 if (pSemaphore == NULL) {
17712 return;
17713 }
17714
17715 pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond);
17716 pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock);
17717}
17718
17719static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore)
17720{
17721 if (pSemaphore == NULL) {
17722 return MA_INVALID_ARGS;
17723 }
17724
17725 pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock);
17726 {
17727 /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */
17728 while (pSemaphore->value == 0) {
17729 pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock);
17730 }
17731
17732 pSemaphore->value -= 1;
17733 }
17734 pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock);
17735
17736 return MA_SUCCESS;
17737}
17738
17739static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore)
17740{
17741 if (pSemaphore == NULL) {
17742 return MA_INVALID_ARGS;
17743 }
17744
17745 pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock);
17746 {
17747 pSemaphore->value += 1;
17748 pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond);
17749 }
17750 pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock);
17751
17752 return MA_SUCCESS;
17753}
17754#elif defined(MA_WIN32)
17755static int ma_thread_priority_to_win32(ma_thread_priority priority)
17756{
17757 switch (priority) {
17758 case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE;
17759 case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST;
17760 case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL;
17761 case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL;
17762 case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL;
17763 case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST;
17764 case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL;
17765 default: return THREAD_PRIORITY_NORMAL;
17766 }
17767}
17768
17769static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)
17770{
17771 DWORD threadID; /* Not used. Only used for passing into CreateThread() so it doesn't fail on Windows 98. */
17772
17773 *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, &threadID);
17774 if (*pThread == NULL) {
17775 return ma_result_from_GetLastError(GetLastError());
17776 }
17777
17778 SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority));
17779
17780 return MA_SUCCESS;
17781}
17782
17783static void ma_thread_wait__win32(ma_thread* pThread)
17784{
17785 WaitForSingleObject((HANDLE)*pThread, INFINITE);
17786 CloseHandle((HANDLE)*pThread);
17787}
17788
17789
17790static ma_result ma_mutex_init__win32(ma_mutex* pMutex)
17791{
17792 *pMutex = CreateEventA(NULL, FALSE, TRUE, NULL);
17793 if (*pMutex == NULL) {
17794 return ma_result_from_GetLastError(GetLastError());
17795 }
17796
17797 return MA_SUCCESS;
17798}
17799
17800static void ma_mutex_uninit__win32(ma_mutex* pMutex)
17801{
17802 CloseHandle((HANDLE)*pMutex);
17803}
17804
17805static void ma_mutex_lock__win32(ma_mutex* pMutex)
17806{
17807 WaitForSingleObject((HANDLE)*pMutex, INFINITE);
17808}
17809
17810static void ma_mutex_unlock__win32(ma_mutex* pMutex)
17811{
17812 SetEvent((HANDLE)*pMutex);
17813}
17814
17815
17816static ma_result ma_event_init__win32(ma_event* pEvent)
17817{
17818 *pEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
17819 if (*pEvent == NULL) {
17820 return ma_result_from_GetLastError(GetLastError());
17821 }
17822
17823 return MA_SUCCESS;
17824}
17825
17826static void ma_event_uninit__win32(ma_event* pEvent)
17827{
17828 CloseHandle((HANDLE)*pEvent);
17829}
17830
17831static ma_result ma_event_wait__win32(ma_event* pEvent)
17832{
17833 DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE);
17834 if (result == WAIT_OBJECT_0) {
17835 return MA_SUCCESS;
17836 }
17837
17838 if (result == WAIT_TIMEOUT) {
17839 return MA_TIMEOUT;
17840 }
17841
17842 return ma_result_from_GetLastError(GetLastError());
17843}
17844
17845static ma_result ma_event_signal__win32(ma_event* pEvent)
17846{
17847 BOOL result = SetEvent((HANDLE)*pEvent);
17848 if (result == 0) {
17849 return ma_result_from_GetLastError(GetLastError());
17850 }
17851
17852 return MA_SUCCESS;
17853}
17854
17855
17856static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore)
17857{
17858 *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL);
17859 if (*pSemaphore == NULL) {
17860 return ma_result_from_GetLastError(GetLastError());
17861 }
17862
17863 return MA_SUCCESS;
17864}
17865
17866static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore)
17867{
17868 CloseHandle((HANDLE)*pSemaphore);
17869}
17870
17871static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore)
17872{
17873 DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE);
17874 if (result == WAIT_OBJECT_0) {
17875 return MA_SUCCESS;
17876 }
17877
17878 if (result == WAIT_TIMEOUT) {
17879 return MA_TIMEOUT;
17880 }
17881
17882 return ma_result_from_GetLastError(GetLastError());
17883}
17884
17885static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore)
17886{
17887 BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL);
17888 if (result == 0) {
17889 return ma_result_from_GetLastError(GetLastError());
17890 }
17891
17892 return MA_SUCCESS;
17893}
17894#endif
17895
17896typedef struct
17897{
17898 ma_thread_entry_proc entryProc;
17899 void* pData;
17900 ma_allocation_callbacks allocationCallbacks;
17901} ma_thread_proxy_data;
17902
17903static ma_thread_result MA_THREADCALL ma_thread_entry_proxy(void* pData)
17904{
17905 ma_thread_proxy_data* pProxyData = (ma_thread_proxy_data*)pData;
17906 ma_thread_entry_proc entryProc;
17907 void* pEntryProcData;
17908 ma_thread_result result;
17909
17910 #if defined(MA_ON_THREAD_ENTRY)
17911 MA_ON_THREAD_ENTRY
17912 #endif
17913
17914 entryProc = pProxyData->entryProc;
17915 pEntryProcData = pProxyData->pData;
17916
17917 /* Free the proxy data before getting into the real thread entry proc. */
17918 ma_free(pProxyData, &pProxyData->allocationCallbacks);
17919
17920 result = entryProc(pEntryProcData);
17921
17922 #if defined(MA_ON_THREAD_EXIT)
17923 MA_ON_THREAD_EXIT
17924 #endif
17925
17926 return result;
17927}
17928
17929static 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)
17930{
17931 ma_result result;
17932 ma_thread_proxy_data* pProxyData;
17933
17934 if (pThread == NULL || entryProc == NULL) {
17935 return MA_INVALID_ARGS;
17936 }
17937
17938 pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks); /* Will be freed by the proxy entry proc. */
17939 if (pProxyData == NULL) {
17940 return MA_OUT_OF_MEMORY;
17941 }
17942
17943#if defined(MA_THREAD_DEFAULT_STACK_SIZE)
17944 if (stackSize == 0) {
17945 stackSize = MA_THREAD_DEFAULT_STACK_SIZE;
17946 }
17947#endif
17948
17949 pProxyData->entryProc = entryProc;
17950 pProxyData->pData = pData;
17951 ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks);
17952
17953#if defined(MA_POSIX)
17954 result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData);
17955#elif defined(MA_WIN32)
17956 result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData);
17957#endif
17958
17959 if (result != MA_SUCCESS) {
17960 ma_free(pProxyData, pAllocationCallbacks);
17961 return result;
17962 }
17963
17964 return MA_SUCCESS;
17965}
17966
17967static void ma_thread_wait(ma_thread* pThread)
17968{
17969 if (pThread == NULL) {
17970 return;
17971 }
17972
17973#if defined(MA_POSIX)
17974 ma_thread_wait__posix(pThread);
17975#elif defined(MA_WIN32)
17976 ma_thread_wait__win32(pThread);
17977#endif
17978}
17979
17980
17982{
17983 if (pMutex == NULL) {
17984 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
17985 return MA_INVALID_ARGS;
17986 }
17987
17988#if defined(MA_POSIX)
17989 return ma_mutex_init__posix(pMutex);
17990#elif defined(MA_WIN32)
17991 return ma_mutex_init__win32(pMutex);
17992#endif
17993}
17994
17995MA_API void ma_mutex_uninit(ma_mutex* pMutex)
17996{
17997 if (pMutex == NULL) {
17998 return;
17999 }
18000
18001#if defined(MA_POSIX)
18002 ma_mutex_uninit__posix(pMutex);
18003#elif defined(MA_WIN32)
18004 ma_mutex_uninit__win32(pMutex);
18005#endif
18006}
18007
18008MA_API void ma_mutex_lock(ma_mutex* pMutex)
18009{
18010 if (pMutex == NULL) {
18011 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
18012 return;
18013 }
18014
18015#if defined(MA_POSIX)
18016 ma_mutex_lock__posix(pMutex);
18017#elif defined(MA_WIN32)
18018 ma_mutex_lock__win32(pMutex);
18019#endif
18020}
18021
18022MA_API void ma_mutex_unlock(ma_mutex* pMutex)
18023{
18024 if (pMutex == NULL) {
18025 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
18026 return;
18027 }
18028
18029#if defined(MA_POSIX)
18030 ma_mutex_unlock__posix(pMutex);
18031#elif defined(MA_WIN32)
18032 ma_mutex_unlock__win32(pMutex);
18033#endif
18034}
18035
18036
18038{
18039 if (pEvent == NULL) {
18040 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
18041 return MA_INVALID_ARGS;
18042 }
18043
18044#if defined(MA_POSIX)
18045 return ma_event_init__posix(pEvent);
18046#elif defined(MA_WIN32)
18047 return ma_event_init__win32(pEvent);
18048#endif
18049}
18050
18051#if 0
18052static ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callbacks* pAllocationCallbacks)
18053{
18054 ma_result result;
18055 ma_event* pEvent;
18056
18057 if (ppEvent == NULL) {
18058 return MA_INVALID_ARGS;
18059 }
18060
18061 *ppEvent = NULL;
18062
18063 pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks);
18064 if (pEvent == NULL) {
18065 return MA_OUT_OF_MEMORY;
18066 }
18067
18068 result = ma_event_init(pEvent);
18069 if (result != MA_SUCCESS) {
18070 ma_free(pEvent, pAllocationCallbacks);
18071 return result;
18072 }
18073
18074 *ppEvent = pEvent;
18075 return result;
18076}
18077#endif
18078
18079MA_API void ma_event_uninit(ma_event* pEvent)
18080{
18081 if (pEvent == NULL) {
18082 return;
18083 }
18084
18085#if defined(MA_POSIX)
18086 ma_event_uninit__posix(pEvent);
18087#elif defined(MA_WIN32)
18088 ma_event_uninit__win32(pEvent);
18089#endif
18090}
18091
18092#if 0
18093static void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* pAllocationCallbacks)
18094{
18095 if (pEvent == NULL) {
18096 return;
18097 }
18098
18099 ma_event_uninit(pEvent);
18100 ma_free(pEvent, pAllocationCallbacks);
18101}
18102#endif
18103
18105{
18106 if (pEvent == NULL) {
18107 MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */
18108 return MA_INVALID_ARGS;
18109 }
18110
18111#if defined(MA_POSIX)
18112 return ma_event_wait__posix(pEvent);
18113#elif defined(MA_WIN32)
18114 return ma_event_wait__win32(pEvent);
18115#endif
18116}
18117
18119{
18120 if (pEvent == NULL) {
18121 MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */
18122 return MA_INVALID_ARGS;
18123 }
18124
18125#if defined(MA_POSIX)
18126 return ma_event_signal__posix(pEvent);
18127#elif defined(MA_WIN32)
18128 return ma_event_signal__win32(pEvent);
18129#endif
18130}
18131
18132
18133MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore)
18134{
18135 if (pSemaphore == NULL) {
18136 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
18137 return MA_INVALID_ARGS;
18138 }
18139
18140#if defined(MA_POSIX)
18141 return ma_semaphore_init__posix(initialValue, pSemaphore);
18142#elif defined(MA_WIN32)
18143 return ma_semaphore_init__win32(initialValue, pSemaphore);
18144#endif
18145}
18146
18147MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore)
18148{
18149 if (pSemaphore == NULL) {
18150 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
18151 return;
18152 }
18153
18154#if defined(MA_POSIX)
18155 ma_semaphore_uninit__posix(pSemaphore);
18156#elif defined(MA_WIN32)
18157 ma_semaphore_uninit__win32(pSemaphore);
18158#endif
18159}
18160
18162{
18163 if (pSemaphore == NULL) {
18164 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
18165 return MA_INVALID_ARGS;
18166 }
18167
18168#if defined(MA_POSIX)
18169 return ma_semaphore_wait__posix(pSemaphore);
18170#elif defined(MA_WIN32)
18171 return ma_semaphore_wait__win32(pSemaphore);
18172#endif
18173}
18174
18176{
18177 if (pSemaphore == NULL) {
18178 MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */
18179 return MA_INVALID_ARGS;
18180 }
18181
18182#if defined(MA_POSIX)
18183 return ma_semaphore_release__posix(pSemaphore);
18184#elif defined(MA_WIN32)
18185 return ma_semaphore_release__win32(pSemaphore);
18186#endif
18187}
18188#else
18189/* 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. */
18190#ifndef MA_NO_DEVICE_IO
18191#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO";
18192#endif
18193#endif /* MA_NO_THREADING */
18194
18195
18196
18197#define MA_FENCE_COUNTER_MAX 0x7FFFFFFF
18198
18200{
18201 if (pFence == NULL) {
18202 return MA_INVALID_ARGS;
18203 }
18204
18205 MA_ZERO_OBJECT(pFence);
18206 pFence->counter = 0;
18207
18208 #ifndef MA_NO_THREADING
18209 {
18210 ma_result result;
18211
18212 result = ma_event_init(&pFence->e);
18213 if (result != MA_SUCCESS) {
18214 return result;
18215 }
18216 }
18217 #endif
18218
18219 return MA_SUCCESS;
18220}
18221
18222MA_API void ma_fence_uninit(ma_fence* pFence)
18223{
18224 if (pFence == NULL) {
18225 return;
18226 }
18227
18228 #ifndef MA_NO_THREADING
18229 {
18230 ma_event_uninit(&pFence->e);
18231 }
18232 #endif
18233
18234 MA_ZERO_OBJECT(pFence);
18235}
18236
18238{
18239 if (pFence == NULL) {
18240 return MA_INVALID_ARGS;
18241 }
18242
18243 for (;;) {
18244 ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter);
18245 ma_uint32 newCounter = oldCounter + 1;
18246
18247 /* Make sure we're not about to exceed our maximum value. */
18248 if (newCounter > MA_FENCE_COUNTER_MAX) {
18249 MA_ASSERT(MA_FALSE);
18250 return MA_OUT_OF_RANGE;
18251 }
18252
18253 if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) {
18254 return MA_SUCCESS;
18255 } else {
18256 if (oldCounter == MA_FENCE_COUNTER_MAX) {
18257 MA_ASSERT(MA_FALSE);
18258 return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */
18259 }
18260 }
18261 }
18262
18263 /* Should never get here. */
18264 /*return MA_SUCCESS;*/
18265}
18266
18268{
18269 if (pFence == NULL) {
18270 return MA_INVALID_ARGS;
18271 }
18272
18273 for (;;) {
18274 ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter);
18275 ma_uint32 newCounter = oldCounter - 1;
18276
18277 if (oldCounter == 0) {
18278 MA_ASSERT(MA_FALSE);
18279 return MA_INVALID_OPERATION; /* Acquire/release mismatch. */
18280 }
18281
18282 if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) {
18283 #ifndef MA_NO_THREADING
18284 {
18285 if (newCounter == 0) {
18286 ma_event_signal(&pFence->e); /* <-- ma_fence_wait() will be waiting on this. */
18287 }
18288 }
18289 #endif
18290
18291 return MA_SUCCESS;
18292 } else {
18293 if (oldCounter == 0) {
18294 MA_ASSERT(MA_FALSE);
18295 return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */
18296 }
18297 }
18298 }
18299
18300 /* Should never get here. */
18301 /*return MA_SUCCESS;*/
18302}
18303
18305{
18306 if (pFence == NULL) {
18307 return MA_INVALID_ARGS;
18308 }
18309
18310 for (;;) {
18311 ma_uint32 counter;
18312
18313 counter = ma_atomic_load_32(&pFence->counter);
18314 if (counter == 0) {
18315 /*
18316 Counter has hit zero. By the time we get here some other thread may have acquired the
18317 fence again, but that is where the caller needs to take care with how they se the fence.
18318 */
18319 return MA_SUCCESS;
18320 }
18321
18322 /* Getting here means the counter is > 0. We'll need to wait for something to happen. */
18323 #ifndef MA_NO_THREADING
18324 {
18325 ma_result result;
18326
18327 result = ma_event_wait(&pFence->e);
18328 if (result != MA_SUCCESS) {
18329 return result;
18330 }
18331 }
18332 #endif
18333 }
18334
18335 /* Should never get here. */
18336 /*return MA_INVALID_OPERATION;*/
18337}
18338
18339
18341{
18342 ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification;
18343
18344 if (pNotification == NULL) {
18345 return MA_INVALID_ARGS;
18346 }
18347
18348 if (pNotificationCallbacks->onSignal == NULL) {
18349 return MA_NOT_IMPLEMENTED;
18350 }
18351
18352 pNotificationCallbacks->onSignal(pNotification);
18353 return MA_INVALID_ARGS;
18354}
18355
18356
18357static void ma_async_notification_poll__on_signal(ma_async_notification* pNotification)
18358{
18359 ((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE;
18360}
18361
18363{
18364 if (pNotificationPoll == NULL) {
18365 return MA_INVALID_ARGS;
18366 }
18367
18368 pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal;
18369 pNotificationPoll->signalled = MA_FALSE;
18370
18371 return MA_SUCCESS;
18372}
18373
18375{
18376 if (pNotificationPoll == NULL) {
18377 return MA_FALSE;
18378 }
18379
18380 return pNotificationPoll->signalled;
18381}
18382
18383
18384static void ma_async_notification_event__on_signal(ma_async_notification* pNotification)
18385{
18387}
18388
18390{
18391 if (pNotificationEvent == NULL) {
18392 return MA_INVALID_ARGS;
18393 }
18394
18395 pNotificationEvent->cb.onSignal = ma_async_notification_event__on_signal;
18396
18397 #ifndef MA_NO_THREADING
18398 {
18399 ma_result result;
18400
18401 result = ma_event_init(&pNotificationEvent->e);
18402 if (result != MA_SUCCESS) {
18403 return result;
18404 }
18405
18406 return MA_SUCCESS;
18407 }
18408 #else
18409 {
18410 return MA_NOT_IMPLEMENTED; /* Threading is disabled. */
18411 }
18412 #endif
18413}
18414
18416{
18417 if (pNotificationEvent == NULL) {
18418 return MA_INVALID_ARGS;
18419 }
18420
18421 #ifndef MA_NO_THREADING
18422 {
18423 ma_event_uninit(&pNotificationEvent->e);
18424 return MA_SUCCESS;
18425 }
18426 #else
18427 {
18428 return MA_NOT_IMPLEMENTED; /* Threading is disabled. */
18429 }
18430 #endif
18431}
18432
18434{
18435 if (pNotificationEvent == NULL) {
18436 return MA_INVALID_ARGS;
18437 }
18438
18439 #ifndef MA_NO_THREADING
18440 {
18441 return ma_event_wait(&pNotificationEvent->e);
18442 }
18443 #else
18444 {
18445 return MA_NOT_IMPLEMENTED; /* Threading is disabled. */
18446 }
18447 #endif
18448}
18449
18451{
18452 if (pNotificationEvent == NULL) {
18453 return MA_INVALID_ARGS;
18454 }
18455
18456 #ifndef MA_NO_THREADING
18457 {
18458 return ma_event_signal(&pNotificationEvent->e);
18459 }
18460 #else
18461 {
18462 return MA_NOT_IMPLEMENTED; /* Threading is disabled. */
18463 }
18464 #endif
18465}
18466
18467
18468
18469/************************************************************************************************************************************************************
18470
18471Job Queue
18472
18473************************************************************************************************************************************************************/
18475{
18477
18478 MA_ZERO_OBJECT(&config);
18479 config.capacity = capacity;
18480
18481 return config;
18482}
18483
18484
18485static MA_INLINE ma_uint32 ma_slot_allocator_calculate_group_capacity(ma_uint32 slotCapacity)
18486{
18487 ma_uint32 cap = slotCapacity / 32;
18488 if ((slotCapacity % 32) != 0) {
18489 cap += 1;
18490 }
18491
18492 return cap;
18493}
18494
18495static MA_INLINE ma_uint32 ma_slot_allocator_group_capacity(const ma_slot_allocator* pAllocator)
18496{
18497 return ma_slot_allocator_calculate_group_capacity(pAllocator->capacity);
18498}
18499
18500
18501typedef struct
18502{
18503 size_t sizeInBytes;
18504 size_t groupsOffset;
18505 size_t slotsOffset;
18506} ma_slot_allocator_heap_layout;
18507
18508static ma_result ma_slot_allocator_get_heap_layout(const ma_slot_allocator_config* pConfig, ma_slot_allocator_heap_layout* pHeapLayout)
18509{
18510 MA_ASSERT(pHeapLayout != NULL);
18511
18512 MA_ZERO_OBJECT(pHeapLayout);
18513
18514 if (pConfig == NULL) {
18515 return MA_INVALID_ARGS;
18516 }
18517
18518 if (pConfig->capacity == 0) {
18519 return MA_INVALID_ARGS;
18520 }
18521
18522 pHeapLayout->sizeInBytes = 0;
18523
18524 /* Groups. */
18525 pHeapLayout->groupsOffset = pHeapLayout->sizeInBytes;
18526 pHeapLayout->sizeInBytes += ma_align_64(ma_slot_allocator_calculate_group_capacity(pConfig->capacity) * sizeof(ma_slot_allocator_group));
18527
18528 /* Slots. */
18529 pHeapLayout->slotsOffset = pHeapLayout->sizeInBytes;
18530 pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_uint32));
18531
18532 return MA_SUCCESS;
18533}
18534
18535MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes)
18536{
18537 ma_result result;
18538 ma_slot_allocator_heap_layout layout;
18539
18540 if (pHeapSizeInBytes == NULL) {
18541 return MA_INVALID_ARGS;
18542 }
18543
18544 *pHeapSizeInBytes = 0;
18545
18546 result = ma_slot_allocator_get_heap_layout(pConfig, &layout);
18547 if (result != MA_SUCCESS) {
18548 return result;
18549 }
18550
18551 *pHeapSizeInBytes = layout.sizeInBytes;
18552
18553 return result;
18554}
18555
18557{
18558 ma_result result;
18559 ma_slot_allocator_heap_layout heapLayout;
18560
18561 if (pAllocator == NULL) {
18562 return MA_INVALID_ARGS;
18563 }
18564
18565 MA_ZERO_OBJECT(pAllocator);
18566
18567 if (pHeap == NULL) {
18568 return MA_INVALID_ARGS;
18569 }
18570
18571 result = ma_slot_allocator_get_heap_layout(pConfig, &heapLayout);
18572 if (result != MA_SUCCESS) {
18573 return result;
18574 }
18575
18576 pAllocator->_pHeap = pHeap;
18577 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
18578
18579 pAllocator->pGroups = (ma_slot_allocator_group*)ma_offset_ptr(pHeap, heapLayout.groupsOffset);
18580 pAllocator->pSlots = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.slotsOffset);
18581 pAllocator->capacity = pConfig->capacity;
18582
18583 return MA_SUCCESS;
18584}
18585
18586MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator)
18587{
18588 ma_result result;
18589 size_t heapSizeInBytes;
18590 void* pHeap;
18591
18592 result = ma_slot_allocator_get_heap_size(pConfig, &heapSizeInBytes);
18593 if (result != MA_SUCCESS) {
18594 return result; /* Failed to retrieve the size of the heap allocation. */
18595 }
18596
18597 if (heapSizeInBytes > 0) {
18598 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
18599 if (pHeap == NULL) {
18600 return MA_OUT_OF_MEMORY;
18601 }
18602 } else {
18603 pHeap = NULL;
18604 }
18605
18606 result = ma_slot_allocator_init_preallocated(pConfig, pHeap, pAllocator);
18607 if (result != MA_SUCCESS) {
18608 ma_free(pHeap, pAllocationCallbacks);
18609 return result;
18610 }
18611
18612 pAllocator->_ownsHeap = MA_TRUE;
18613 return MA_SUCCESS;
18614}
18615
18616MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks)
18617{
18618 if (pAllocator == NULL) {
18619 return;
18620 }
18621
18622 if (pAllocator->_ownsHeap) {
18623 ma_free(pAllocator->_pHeap, pAllocationCallbacks);
18624 }
18625}
18626
18628{
18629 ma_uint32 iAttempt;
18630 const ma_uint32 maxAttempts = 2; /* The number of iterations to perform until returning MA_OUT_OF_MEMORY if no slots can be found. */
18631
18632 if (pAllocator == NULL || pSlot == NULL) {
18633 return MA_INVALID_ARGS;
18634 }
18635
18636 for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) {
18637 /* We need to acquire a suitable bitfield first. This is a bitfield that's got an available slot within it. */
18638 ma_uint32 iGroup;
18639 for (iGroup = 0; iGroup < ma_slot_allocator_group_capacity(pAllocator); iGroup += 1) {
18640 /* CAS */
18641 for (;;) {
18642 ma_uint32 oldBitfield;
18643 ma_uint32 newBitfield;
18644 ma_uint32 bitOffset;
18645
18646 oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */
18647
18648 /* Fast check to see if anything is available. */
18649 if (oldBitfield == 0xFFFFFFFF) {
18650 break; /* No available bits in this bitfield. */
18651 }
18652
18653 bitOffset = ma_ffs_32(~oldBitfield);
18654 MA_ASSERT(bitOffset < 32);
18655
18656 newBitfield = oldBitfield | (1 << bitOffset);
18657
18658 if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) {
18659 ma_uint32 slotIndex;
18660
18661 /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */
18662 ma_atomic_fetch_add_32(&pAllocator->count, 1);
18663
18664 /* The slot index is required for constructing the output value. */
18665 slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */
18666 if (slotIndex >= pAllocator->capacity) {
18667 return MA_OUT_OF_MEMORY;
18668 }
18669
18670 /* Increment the reference count before constructing the output value. */
18671 pAllocator->pSlots[slotIndex] += 1;
18672
18673 /* Construct the output value. */
18674 *pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex);
18675
18676 return MA_SUCCESS;
18677 }
18678 }
18679 }
18680
18681 /* 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. */
18682 if (pAllocator->count < pAllocator->capacity) {
18683 ma_yield();
18684 } else {
18685 return MA_OUT_OF_MEMORY;
18686 }
18687 }
18688
18689 /* We couldn't find a slot within the maximum number of attempts. */
18690 return MA_OUT_OF_MEMORY;
18691}
18692
18694{
18695 ma_uint32 iGroup;
18696 ma_uint32 iBit;
18697
18698 if (pAllocator == NULL) {
18699 return MA_INVALID_ARGS;
18700 }
18701
18702 iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5); /* slot / 32 */
18703 iBit = (ma_uint32)((slot & 0xFFFFFFFF) & 31); /* slot % 32 */
18704
18705 if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) {
18706 return MA_INVALID_ARGS;
18707 }
18708
18709 MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */
18710
18711 while (ma_atomic_load_32(&pAllocator->count) > 0) {
18712 /* CAS */
18713 ma_uint32 oldBitfield;
18714 ma_uint32 newBitfield;
18715
18716 oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */
18717 newBitfield = oldBitfield & ~(1 << iBit);
18718
18719 /* Debugging for checking for double-frees. */
18720 #if defined(MA_DEBUG_OUTPUT)
18721 {
18722 if ((oldBitfield & (1 << iBit)) == 0) {
18723 MA_ASSERT(MA_FALSE); /* Double free detected.*/
18724 }
18725 }
18726 #endif
18727
18728 if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) {
18729 ma_atomic_fetch_sub_32(&pAllocator->count, 1);
18730 return MA_SUCCESS;
18731 }
18732 }
18733
18734 /* Getting here means there are no allocations available for freeing. */
18735 return MA_INVALID_OPERATION;
18736}
18737
18738
18739#define MA_JOB_ID_NONE ~((ma_uint64)0)
18740#define MA_JOB_SLOT_NONE (ma_uint16)(~0)
18741
18742static MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc)
18743{
18744 return (ma_uint32)(toc >> 32);
18745}
18746
18747static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc)
18748{
18749 return (ma_uint16)(toc & 0x0000FFFF);
18750}
18751
18752#if 0 /* Currently unused, but might make use of this later. */
18753static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc)
18754{
18755 return (ma_uint16)((toc & 0xFFFF0000) >> 16);
18756}
18757#endif
18758
18759static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc)
18760{
18761 return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc);
18762}
18763
18764static MA_INLINE ma_uint64 ma_job_set_refcount(ma_uint64 toc, ma_uint32 refcount)
18765{
18766 /* Clear the reference count first. */
18767 toc = toc & ~((ma_uint64)0xFFFFFFFF << 32);
18768 toc = toc | ((ma_uint64)refcount << 32);
18769
18770 return toc;
18771}
18772
18773
18775{
18776 ma_job job;
18777
18778 MA_ZERO_OBJECT(&job);
18779 job.toc.breakup.code = code;
18780 job.toc.breakup.slot = MA_JOB_SLOT_NONE; /* Temp value. Will be allocated when posted to a queue. */
18781 job.next = MA_JOB_ID_NONE;
18782
18783 return job;
18784}
18785
18786
18787static ma_result ma_job_process__noop(ma_job* pJob);
18788static ma_result ma_job_process__quit(ma_job* pJob);
18789static ma_result ma_job_process__custom(ma_job* pJob);
18790static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob);
18791static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob);
18792static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob);
18793static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob);
18794static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob);
18795static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob);
18796static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob);
18797static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob);
18798static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob);
18799
18800#if !defined(MA_NO_DEVICE_IO)
18801static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob);
18802#endif
18803
18804static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] =
18805{
18806 /* Miscellaneous. */
18807 ma_job_process__quit, /* MA_JOB_TYPE_QUIT */
18808 ma_job_process__custom, /* MA_JOB_TYPE_CUSTOM */
18809
18810 /* Resource Manager. */
18811 ma_job_process__resource_manager__load_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE */
18812 ma_job_process__resource_manager__free_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE */
18813 ma_job_process__resource_manager__page_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE */
18814 ma_job_process__resource_manager__load_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER */
18815 ma_job_process__resource_manager__free_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER */
18816 ma_job_process__resource_manager__load_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM */
18817 ma_job_process__resource_manager__free_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM */
18818 ma_job_process__resource_manager__page_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM */
18819 ma_job_process__resource_manager__seek_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM */
18820
18821 /* Device. */
18822#if !defined(MA_NO_DEVICE_IO)
18823 ma_job_process__device__aaudio_reroute /* MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE */
18824#endif
18825};
18826
18828{
18829 if (pJob == NULL) {
18830 return MA_INVALID_ARGS;
18831 }
18832
18833 if (pJob->toc.breakup.code >= MA_JOB_TYPE_COUNT) {
18834 return MA_INVALID_OPERATION;
18835 }
18836
18837 return g_jobVTable[pJob->toc.breakup.code](pJob);
18838}
18839
18840static ma_result ma_job_process__noop(ma_job* pJob)
18841{
18842 MA_ASSERT(pJob != NULL);
18843
18844 /* No-op. */
18845 (void)pJob;
18846
18847 return MA_SUCCESS;
18848}
18849
18850static ma_result ma_job_process__quit(ma_job* pJob)
18851{
18852 return ma_job_process__noop(pJob);
18853}
18854
18855static ma_result ma_job_process__custom(ma_job* pJob)
18856{
18857 MA_ASSERT(pJob != NULL);
18858
18859 /* No-op if there's no callback. */
18860 if (pJob->data.custom.proc == NULL) {
18861 return MA_SUCCESS;
18862 }
18863
18864 return pJob->data.custom.proc(pJob);
18865}
18866
18867
18868
18870{
18871 ma_job_queue_config config;
18872
18873 config.flags = flags;
18874 config.capacity = capacity;
18875
18876 return config;
18877}
18878
18879
18880typedef struct
18881{
18882 size_t sizeInBytes;
18883 size_t allocatorOffset;
18884 size_t jobsOffset;
18885} ma_job_queue_heap_layout;
18886
18887static ma_result ma_job_queue_get_heap_layout(const ma_job_queue_config* pConfig, ma_job_queue_heap_layout* pHeapLayout)
18888{
18889 ma_result result;
18890
18891 MA_ASSERT(pHeapLayout != NULL);
18892
18893 MA_ZERO_OBJECT(pHeapLayout);
18894
18895 if (pConfig == NULL) {
18896 return MA_INVALID_ARGS;
18897 }
18898
18899 if (pConfig->capacity == 0) {
18900 return MA_INVALID_ARGS;
18901 }
18902
18903 pHeapLayout->sizeInBytes = 0;
18904
18905 /* Allocator. */
18906 {
18907 ma_slot_allocator_config allocatorConfig;
18908 size_t allocatorHeapSizeInBytes;
18909
18910 allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity);
18911 result = ma_slot_allocator_get_heap_size(&allocatorConfig, &allocatorHeapSizeInBytes);
18912 if (result != MA_SUCCESS) {
18913 return result;
18914 }
18915
18916 pHeapLayout->allocatorOffset = pHeapLayout->sizeInBytes;
18917 pHeapLayout->sizeInBytes += allocatorHeapSizeInBytes;
18918 }
18919
18920 /* Jobs. */
18921 pHeapLayout->jobsOffset = pHeapLayout->sizeInBytes;
18922 pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_job));
18923
18924 return MA_SUCCESS;
18925}
18926
18927MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes)
18928{
18929 ma_result result;
18930 ma_job_queue_heap_layout layout;
18931
18932 if (pHeapSizeInBytes == NULL) {
18933 return MA_INVALID_ARGS;
18934 }
18935
18936 *pHeapSizeInBytes = 0;
18937
18938 result = ma_job_queue_get_heap_layout(pConfig, &layout);
18939 if (result != MA_SUCCESS) {
18940 return result;
18941 }
18942
18943 *pHeapSizeInBytes = layout.sizeInBytes;
18944
18945 return MA_SUCCESS;
18946}
18947
18949{
18950 ma_result result;
18951 ma_job_queue_heap_layout heapLayout;
18952 ma_slot_allocator_config allocatorConfig;
18953
18954 if (pQueue == NULL) {
18955 return MA_INVALID_ARGS;
18956 }
18957
18958 MA_ZERO_OBJECT(pQueue);
18959
18960 result = ma_job_queue_get_heap_layout(pConfig, &heapLayout);
18961 if (result != MA_SUCCESS) {
18962 return result;
18963 }
18964
18965 pQueue->_pHeap = pHeap;
18966 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
18967
18968 pQueue->flags = pConfig->flags;
18969 pQueue->capacity = pConfig->capacity;
18970 pQueue->pJobs = (ma_job*)ma_offset_ptr(pHeap, heapLayout.jobsOffset);
18971
18972 allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity);
18973 result = ma_slot_allocator_init_preallocated(&allocatorConfig, ma_offset_ptr(pHeap, heapLayout.allocatorOffset), &pQueue->allocator);
18974 if (result != MA_SUCCESS) {
18975 return result;
18976 }
18977
18978 /* We need a semaphore if we're running in non-blocking mode. If threading is disabled we need to return an error. */
18979 if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
18980 #ifndef MA_NO_THREADING
18981 {
18982 ma_semaphore_init(0, &pQueue->sem);
18983 }
18984 #else
18985 {
18986 /* Threading is disabled and we've requested non-blocking mode. */
18987 return MA_INVALID_OPERATION;
18988 }
18989 #endif
18990 }
18991
18992 /*
18993 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
18994 just a dummy item for giving us the first item in the list which is stored in the "next" member.
18995 */
18996 ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head); /* Will never fail. */
18997 pQueue->pJobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE;
18998 pQueue->tail = pQueue->head;
18999
19000 return MA_SUCCESS;
19001}
19002
19003MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue)
19004{
19005 ma_result result;
19006 size_t heapSizeInBytes;
19007 void* pHeap;
19008
19009 result = ma_job_queue_get_heap_size(pConfig, &heapSizeInBytes);
19010 if (result != MA_SUCCESS) {
19011 return result;
19012 }
19013
19014 if (heapSizeInBytes > 0) {
19015 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
19016 if (pHeap == NULL) {
19017 return MA_OUT_OF_MEMORY;
19018 }
19019 } else {
19020 pHeap = NULL;
19021 }
19022
19023 result = ma_job_queue_init_preallocated(pConfig, pHeap, pQueue);
19024 if (result != MA_SUCCESS) {
19025 ma_free(pHeap, pAllocationCallbacks);
19026 return result;
19027 }
19028
19029 pQueue->_ownsHeap = MA_TRUE;
19030 return MA_SUCCESS;
19031}
19032
19033MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks)
19034{
19035 if (pQueue == NULL) {
19036 return;
19037 }
19038
19039 /* All we need to do is uninitialize the semaphore. */
19040 if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
19041 #ifndef MA_NO_THREADING
19042 {
19043 ma_semaphore_uninit(&pQueue->sem);
19044 }
19045 #else
19046 {
19047 MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */
19048 }
19049 #endif
19050 }
19051
19052 ma_slot_allocator_uninit(&pQueue->allocator, pAllocationCallbacks);
19053
19054 if (pQueue->_ownsHeap) {
19055 ma_free(pQueue->_pHeap, pAllocationCallbacks);
19056 }
19057}
19058
19059static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired)
19060{
19061 /* The new counter is taken from the expected value. */
19062 return ma_atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected;
19063}
19064
19066{
19067 /*
19068 Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors
19069 */
19070 ma_result result;
19071 ma_uint64 slot;
19072 ma_uint64 tail;
19073 ma_uint64 next;
19074
19075 if (pQueue == NULL || pJob == NULL) {
19076 return MA_INVALID_ARGS;
19077 }
19078
19079 /* We need a new slot. */
19080 result = ma_slot_allocator_alloc(&pQueue->allocator, &slot);
19081 if (result != MA_SUCCESS) {
19082 return result; /* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */
19083 }
19084
19085 /* At this point we should have a slot to place the job. */
19086 MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity);
19087
19088 /* We need to put the job into memory before we do anything. */
19089 pQueue->pJobs[ma_job_extract_slot(slot)] = *pJob;
19090 pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation = slot; /* This will overwrite the job code. */
19091 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. */
19092 pQueue->pJobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE; /* Reset for safety. */
19093
19094 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
19095 ma_spinlock_lock(&pQueue->lock);
19096 #endif
19097 {
19098 /* 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. */
19099 for (;;) {
19100 tail = ma_atomic_load_64(&pQueue->tail);
19101 next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next);
19102
19103 if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->tail))) {
19104 if (ma_job_extract_slot(next) == 0xFFFF) {
19105 if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) {
19106 break;
19107 }
19108 } else {
19109 ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next));
19110 }
19111 }
19112 }
19113 ma_job_queue_cas(&pQueue->tail, tail, slot);
19114 }
19115 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
19116 ma_spinlock_unlock(&pQueue->lock);
19117 #endif
19118
19119
19120 /* Signal the semaphore as the last step if we're using synchronous mode. */
19121 if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
19122 #ifndef MA_NO_THREADING
19123 {
19124 ma_semaphore_release(&pQueue->sem);
19125 }
19126 #else
19127 {
19128 MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */
19129 }
19130 #endif
19131 }
19132
19133 return MA_SUCCESS;
19134}
19135
19137{
19138 ma_uint64 head;
19139 ma_uint64 tail;
19140 ma_uint64 next;
19141
19142 if (pQueue == NULL || pJob == NULL) {
19143 return MA_INVALID_ARGS;
19144 }
19145
19146 /* If we're running in synchronous mode we'll need to wait on a semaphore. */
19147 if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
19148 #ifndef MA_NO_THREADING
19149 {
19150 ma_semaphore_wait(&pQueue->sem);
19151 }
19152 #else
19153 {
19154 MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */
19155 }
19156 #endif
19157 }
19158
19159 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
19160 ma_spinlock_lock(&pQueue->lock);
19161 #endif
19162 {
19163 /*
19164 BUG: In lock-free mode, multiple threads can be in this section of code. The "head" variable in the loop below
19165 is stored. One thread can fall through to the freeing of this item while another is still using "head" for the
19166 retrieval of the "next" variable.
19167
19168 The slot allocator might need to make use of some reference counting to ensure it's only truly freed when
19169 there are no more references to the item. This must be fixed before removing these locks.
19170 */
19171
19172 /* Now we need to remove the root item from the list. */
19173 for (;;) {
19174 head = ma_atomic_load_64(&pQueue->head);
19175 tail = ma_atomic_load_64(&pQueue->tail);
19176 next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next);
19177
19178 if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->head))) {
19179 if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) {
19180 if (ma_job_extract_slot(next) == 0xFFFF) {
19181 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
19182 ma_spinlock_unlock(&pQueue->lock);
19183 #endif
19184 return MA_NO_DATA_AVAILABLE;
19185 }
19186 ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next));
19187 } else {
19188 *pJob = pQueue->pJobs[ma_job_extract_slot(next)];
19189 if (ma_job_queue_cas(&pQueue->head, head, ma_job_extract_slot(next))) {
19190 break;
19191 }
19192 }
19193 }
19194 }
19195 }
19196 #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
19197 ma_spinlock_unlock(&pQueue->lock);
19198 #endif
19199
19200 ma_slot_allocator_free(&pQueue->allocator, head);
19201
19202 /*
19203 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
19204 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
19205 possible.
19206 */
19207 if (pJob->toc.breakup.code == MA_JOB_TYPE_QUIT) {
19208 ma_job_queue_post(pQueue, pJob);
19209 return MA_CANCELLED; /* Return a cancelled status just in case the thread is checking return codes and not properly checking for a quit job. */
19210 }
19211
19212 return MA_SUCCESS;
19213}
19214
19215
19216
19217/*******************************************************************************
19218
19219Dynamic Linking
19220
19221*******************************************************************************/
19222/* Disable run-time linking on certain backends and platforms. */
19223#ifndef MA_NO_RUNTIME_LINKING
19224 #if defined(MA_EMSCRIPTEN) || defined(MA_ORBIS) || defined(MA_PROSPERO) || defined(MA_SWITCH)
19225 #define MA_NO_RUNTIME_LINKING
19226 #endif
19227#endif
19228
19229#ifdef MA_POSIX
19230 /* No need for dlfcn.h if we're not using runtime linking. */
19231 #ifndef MA_NO_RUNTIME_LINKING
19232 #include <dlfcn.h>
19233 #endif
19234#endif
19235
19236MA_API ma_handle ma_dlopen(ma_log* pLog, const char* filename)
19237{
19238#ifndef MA_NO_RUNTIME_LINKING
19239 ma_handle handle;
19240
19241 ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename);
19242
19243 #ifdef MA_WIN32
19244 /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/
19245 #if !defined(MA_WIN32_UWP) || !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)))
19246 handle = (ma_handle)LoadLibraryA(filename);
19247 #else
19248 /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */
19249 WCHAR filenameW[4096];
19250 if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) {
19251 handle = NULL;
19252 } else {
19253 handle = (ma_handle)LoadPackagedLibrary(filenameW, 0);
19254 }
19255 #endif
19256 #else
19257 handle = (ma_handle)dlopen(filename, RTLD_NOW);
19258 #endif
19259
19260 /*
19261 I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority
19262 backend is a deliberate design choice. Instead I'm logging it as an informational message.
19263 */
19264 if (handle == NULL) {
19265 ma_log_postf(pLog, MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename);
19266 }
19267
19268 return handle;
19269#else
19270 /* Runtime linking is disabled. */
19271 (void)pLog;
19272 (void)filename;
19273 return NULL;
19274#endif
19275}
19276
19277MA_API void ma_dlclose(ma_log* pLog, ma_handle handle)
19278{
19279#ifndef MA_NO_RUNTIME_LINKING
19280 #ifdef MA_WIN32
19281 FreeLibrary((HMODULE)handle);
19282 #else
19283 /* Hack for Android bug (see https://github.com/android/ndk/issues/360). Calling dlclose() pre-API 28 may segfault. */
19284 #if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28)
19285 {
19286 dlclose((void*)handle);
19287 }
19288 #else
19289 {
19290 (void)handle;
19291 }
19292 #endif
19293 #endif
19294
19295 (void)pLog;
19296#else
19297 /* Runtime linking is disabled. */
19298 (void)pLog;
19299 (void)handle;
19300#endif
19301}
19302
19303MA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol)
19304{
19305#ifndef MA_NO_RUNTIME_LINKING
19306 ma_proc proc;
19307
19308 ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol);
19309
19310#ifdef _WIN32
19311 proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol);
19312#else
19313#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__)
19314 #pragma GCC diagnostic push
19315 #pragma GCC diagnostic ignored "-Wpedantic"
19316#endif
19317 proc = (ma_proc)dlsym((void*)handle, symbol);
19318#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__)
19319 #pragma GCC diagnostic pop
19320#endif
19321#endif
19322
19323 if (proc == NULL) {
19324 ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol);
19325 }
19326
19327 (void)pLog; /* It's possible for pContext to be unused. */
19328 return proc;
19329#else
19330 /* Runtime linking is disabled. */
19331 (void)pLog;
19332 (void)handle;
19333 (void)symbol;
19334 return NULL;
19335#endif
19336}
19337
19338
19339
19340/************************************************************************************************************************************************************
19341*************************************************************************************************************************************************************
19342
19343DEVICE I/O
19344==========
19345
19346*************************************************************************************************************************************************************
19347************************************************************************************************************************************************************/
19348
19349#ifdef MA_APPLE
19350 #include <AvailabilityMacros.h>
19351#endif
19352
19353#ifndef MA_NO_DEVICE_IO
19354
19355#if defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
19356 #include <mach/mach_time.h> /* For mach_absolute_time() */
19357#endif
19358
19359#ifdef MA_POSIX
19360 #include <sys/types.h>
19361#endif
19362
19363/* This must be set to at least 26. */
19364#ifndef MA_AAUDIO_MIN_ANDROID_SDK_VERSION
19365#define MA_AAUDIO_MIN_ANDROID_SDK_VERSION 27
19366#endif
19367
19368
19369MA_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)
19370{
19371 if (pDeviceInfo == NULL) {
19372 return;
19373 }
19374
19375 if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) {
19376 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
19377 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
19378 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
19379 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
19380 pDeviceInfo->nativeDataFormatCount += 1;
19381 }
19382}
19383
19384
19385typedef struct
19386{
19387 ma_backend backend;
19388 const char* pName;
19389} ma_backend_info;
19390
19391static ma_backend_info gBackendInfo[] = /* Indexed by the backend enum. Must be in the order backends are declared in the ma_backend enum. */
19392{
19393 {ma_backend_wasapi, "WASAPI"},
19394 {ma_backend_dsound, "DirectSound"},
19395 {ma_backend_winmm, "WinMM"},
19396 {ma_backend_coreaudio, "Core Audio"},
19397 {ma_backend_sndio, "sndio"},
19398 {ma_backend_audio4, "audio(4)"},
19399 {ma_backend_oss, "OSS"},
19400 {ma_backend_pulseaudio, "PulseAudio"},
19401 {ma_backend_alsa, "ALSA"},
19402 {ma_backend_jack, "JACK"},
19403 {ma_backend_aaudio, "AAudio"},
19404 {ma_backend_opensl, "OpenSL|ES"},
19405 {ma_backend_webaudio, "Web Audio"},
19406 {ma_backend_custom, "Custom"},
19407 {ma_backend_null, "Null"}
19408};
19409
19410MA_API const char* ma_get_backend_name(ma_backend backend)
19411{
19412 if (backend < 0 || backend >= (int)ma_countof(gBackendInfo)) {
19413 return "Unknown";
19414 }
19415
19416 return gBackendInfo[backend].pName;
19417}
19418
19419MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend)
19420{
19421 size_t iBackend;
19422
19423 if (pBackendName == NULL) {
19424 return MA_INVALID_ARGS;
19425 }
19426
19427 for (iBackend = 0; iBackend < ma_countof(gBackendInfo); iBackend += 1) {
19428 if (ma_strcmp(pBackendName, gBackendInfo[iBackend].pName) == 0) {
19429 if (pBackend != NULL) {
19430 *pBackend = gBackendInfo[iBackend].backend;
19431 }
19432
19433 return MA_SUCCESS;
19434 }
19435 }
19436
19437 /* Getting here means the backend name is unknown. */
19438 return MA_INVALID_ARGS;
19439}
19440
19442{
19443 /*
19444 This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers
19445 about some enums not being handled by the switch statement.
19446 */
19447 switch (backend)
19448 {
19449 case ma_backend_wasapi:
19450 #if defined(MA_HAS_WASAPI)
19451 return MA_TRUE;
19452 #else
19453 return MA_FALSE;
19454 #endif
19455 case ma_backend_dsound:
19456 #if defined(MA_HAS_DSOUND)
19457 return MA_TRUE;
19458 #else
19459 return MA_FALSE;
19460 #endif
19461 case ma_backend_winmm:
19462 #if defined(MA_HAS_WINMM)
19463 return MA_TRUE;
19464 #else
19465 return MA_FALSE;
19466 #endif
19468 #if defined(MA_HAS_COREAUDIO)
19469 return MA_TRUE;
19470 #else
19471 return MA_FALSE;
19472 #endif
19473 case ma_backend_sndio:
19474 #if defined(MA_HAS_SNDIO)
19475 return MA_TRUE;
19476 #else
19477 return MA_FALSE;
19478 #endif
19479 case ma_backend_audio4:
19480 #if defined(MA_HAS_AUDIO4)
19481 return MA_TRUE;
19482 #else
19483 return MA_FALSE;
19484 #endif
19485 case ma_backend_oss:
19486 #if defined(MA_HAS_OSS)
19487 return MA_TRUE;
19488 #else
19489 return MA_FALSE;
19490 #endif
19492 #if defined(MA_HAS_PULSEAUDIO)
19493 return MA_TRUE;
19494 #else
19495 return MA_FALSE;
19496 #endif
19497 case ma_backend_alsa:
19498 #if defined(MA_HAS_ALSA)
19499 return MA_TRUE;
19500 #else
19501 return MA_FALSE;
19502 #endif
19503 case ma_backend_jack:
19504 #if defined(MA_HAS_JACK)
19505 return MA_TRUE;
19506 #else
19507 return MA_FALSE;
19508 #endif
19509 case ma_backend_aaudio:
19510 #if defined(MA_HAS_AAUDIO)
19511 #if defined(MA_ANDROID)
19512 {
19513 return ma_android_sdk_version() >= MA_AAUDIO_MIN_ANDROID_SDK_VERSION;
19514 }
19515 #else
19516 return MA_FALSE;
19517 #endif
19518 #else
19519 return MA_FALSE;
19520 #endif
19521 case ma_backend_opensl:
19522 #if defined(MA_HAS_OPENSL)
19523 #if defined(MA_ANDROID)
19524 {
19525 return ma_android_sdk_version() >= 9;
19526 }
19527 #else
19528 return MA_TRUE;
19529 #endif
19530 #else
19531 return MA_FALSE;
19532 #endif
19534 #if defined(MA_HAS_WEBAUDIO)
19535 return MA_TRUE;
19536 #else
19537 return MA_FALSE;
19538 #endif
19539 case ma_backend_custom:
19540 #if defined(MA_HAS_CUSTOM)
19541 return MA_TRUE;
19542 #else
19543 return MA_FALSE;
19544 #endif
19545 case ma_backend_null:
19546 #if defined(MA_HAS_NULL)
19547 return MA_TRUE;
19548 #else
19549 return MA_FALSE;
19550 #endif
19551
19552 default: return MA_FALSE;
19553 }
19554}
19555
19556MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount)
19557{
19558 size_t backendCount;
19559 size_t iBackend;
19560 ma_result result = MA_SUCCESS;
19561
19562 if (pBackendCount == NULL) {
19563 return MA_INVALID_ARGS;
19564 }
19565
19566 backendCount = 0;
19567
19568 for (iBackend = 0; iBackend <= ma_backend_null; iBackend += 1) {
19569 ma_backend backend = (ma_backend)iBackend;
19570
19571 if (ma_is_backend_enabled(backend)) {
19572 /* The backend is enabled. Try adding it to the list. If there's no room, MA_NO_SPACE needs to be returned. */
19573 if (backendCount == backendCap) {
19574 result = MA_NO_SPACE;
19575 break;
19576 } else {
19577 pBackends[backendCount] = backend;
19578 backendCount += 1;
19579 }
19580 }
19581 }
19582
19583 if (pBackendCount != NULL) {
19584 *pBackendCount = backendCount;
19585 }
19586
19587 return result;
19588}
19589
19591{
19592 switch (backend)
19593 {
19594 case ma_backend_wasapi: return MA_TRUE;
19595 case ma_backend_dsound: return MA_FALSE;
19596 case ma_backend_winmm: return MA_FALSE;
19597 case ma_backend_coreaudio: return MA_FALSE;
19598 case ma_backend_sndio: return MA_FALSE;
19599 case ma_backend_audio4: return MA_FALSE;
19600 case ma_backend_oss: return MA_FALSE;
19601 case ma_backend_pulseaudio: return MA_FALSE;
19602 case ma_backend_alsa: return MA_FALSE;
19603 case ma_backend_jack: return MA_FALSE;
19604 case ma_backend_aaudio: return MA_FALSE;
19605 case ma_backend_opensl: return MA_FALSE;
19606 case ma_backend_webaudio: return MA_FALSE;
19607 case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */
19608 case ma_backend_null: return MA_FALSE;
19609 default: return MA_FALSE;
19610 }
19611}
19612
19613
19614
19615#if defined(MA_WIN32)
19616/* WASAPI error codes. */
19617#define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001)
19618#define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002)
19619#define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003)
19620#define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004)
19621#define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005)
19622#define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006)
19623#define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007)
19624#define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008)
19625#define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009)
19626#define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A)
19627#define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B)
19628#define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C)
19629#define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D)
19630#define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E)
19631#define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F)
19632#define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010)
19633#define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011)
19634#define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012)
19635#define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013)
19636#define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014)
19637#define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015)
19638#define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016)
19639#define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017)
19640#define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018)
19641#define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019)
19642#define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020)
19643#define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021)
19644#define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022)
19645#define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023)
19646#define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024)
19647#define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025)
19648#define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026)
19649#define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027)
19650#define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028)
19651#define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029)
19652#define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030)
19653#define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040)
19654#define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001)
19655#define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002)
19656#define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003)
19657
19658#define MA_DS_OK ((HRESULT)0)
19659#define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A)
19660#define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A)
19661#define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E)
19662#define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/
19663#define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032)
19664#define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/
19665#define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046)
19666#define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/
19667#define MA_DSERR_BADFORMAT ((HRESULT)0x88780064)
19668#define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/
19669#define MA_DSERR_NODRIVER ((HRESULT)0x88780078)
19670#define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082)
19671#define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/
19672#define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096)
19673#define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0)
19674#define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA)
19675#define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/
19676#define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/
19677#define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4)
19678#define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE)
19679#define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8)
19680#define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2)
19681#define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161)
19682#define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC)
19683
19684static ma_result ma_result_from_HRESULT(HRESULT hr)
19685{
19686 switch (hr)
19687 {
19688 case NOERROR: return MA_SUCCESS;
19689 /*case S_OK: return MA_SUCCESS;*/
19690
19691 case E_POINTER: return MA_INVALID_ARGS;
19692 case E_UNEXPECTED: return MA_ERROR;
19693 case E_NOTIMPL: return MA_NOT_IMPLEMENTED;
19694 case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY;
19695 case E_INVALIDARG: return MA_INVALID_ARGS;
19696 case E_NOINTERFACE: return MA_API_NOT_FOUND;
19697 case E_HANDLE: return MA_INVALID_ARGS;
19698 case E_ABORT: return MA_ERROR;
19699 case E_FAIL: return MA_ERROR;
19700 case E_ACCESSDENIED: return MA_ACCESS_DENIED;
19701
19702 /* WASAPI */
19703 case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
19704 case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
19705 case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS;
19706 case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE;
19707 case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED;
19708 case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG;
19709 case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION;
19710 case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED;
19711 case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS;
19712 case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY;
19713 case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION;
19714 case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST;
19715 case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION;
19716 case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED;
19717 case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
19718 case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED;
19719 case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS;
19720 case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED;
19721 case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS;
19722 case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS;
19723 case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS;
19724 case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS;
19725 case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR;
19726 case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR;
19727 case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS;
19728 case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS;
19729 case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS;
19730 case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION;
19731 case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY;
19732 case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
19733 case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
19734 case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA;
19735 case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION;
19736 case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION;
19737 case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION;
19738 case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION;
19739 case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION;
19740 case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE;
19741 case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS;
19742 case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR;
19743
19744 /* DirectSound */
19745 /*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */
19746 case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS;
19747 case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE;
19748 case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION;
19749 /*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */
19750 case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION;
19751 /*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */
19752 case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION;
19753 /*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */
19754 case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED;
19755 /*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */
19756 case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND;
19757 case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
19758 case MA_DSERR_NOAGGREGATION: return MA_ERROR;
19759 case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE;
19760 case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED;
19761 case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
19762 /*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */
19763 /*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */
19764 case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE;
19765 case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION;
19766 case MA_DSERR_SENDLOOP: return MA_DEADLOCK;
19767 case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS;
19768 case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE;
19769 case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE;
19770
19771 default: return MA_ERROR;
19772 }
19773}
19774
19775/* PROPVARIANT */
19776#define MA_VT_LPWSTR 31
19777#define MA_VT_BLOB 65
19778
19779#if defined(_MSC_VER) && !defined(__clang__)
19780 #pragma warning(push)
19781 #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
19782#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
19783 #pragma GCC diagnostic push
19784 #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
19785 #if defined(__clang__)
19786 #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */
19787 #endif
19788#endif
19789typedef struct
19790{
19791 WORD vt;
19792 WORD wReserved1;
19793 WORD wReserved2;
19794 WORD wReserved3;
19795 union
19796 {
19797 struct
19798 {
19799 ULONG cbSize;
19800 BYTE* pBlobData;
19801 } blob;
19802 WCHAR* pwszVal;
19803 char pad[16]; /* Just to ensure the size of the struct matches the official version. */
19804 };
19805} MA_PROPVARIANT;
19806#if defined(_MSC_VER) && !defined(__clang__)
19807 #pragma warning(pop)
19808#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
19809 #pragma GCC diagnostic pop
19810#endif
19811
19812typedef HRESULT (WINAPI * MA_PFN_CoInitialize)(void* pvReserved);
19813typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(void* pvReserved, DWORD dwCoInit);
19814typedef void (WINAPI * MA_PFN_CoUninitialize)(void);
19815typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(const IID* rclsid, void* pUnkOuter, DWORD dwClsContext, const IID* riid, void* ppv);
19816typedef void (WINAPI * MA_PFN_CoTaskMemFree)(void* pv);
19817typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(MA_PROPVARIANT *pvar);
19818typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, WCHAR* lpsz, int cchMax);
19819
19820typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void);
19821typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void);
19822
19823#if defined(MA_WIN32_DESKTOP)
19824/* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */
19825typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, const char* lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult);
19826typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey);
19827typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData);
19828#endif /* MA_WIN32_DESKTOP */
19829
19830MA_API size_t ma_strlen_WCHAR(const WCHAR* str)
19831{
19832 size_t len = 0;
19833 while (str[len] != '\0') {
19834 len += 1;
19835 }
19836
19837 return len;
19838}
19839
19840MA_API int ma_strcmp_WCHAR(const WCHAR *s1, const WCHAR *s2)
19841{
19842 while (*s1 != '\0' && *s1 == *s2) {
19843 s1 += 1;
19844 s2 += 1;
19845 }
19846
19847 return *s1 - *s2;
19848}
19849
19850MA_API int ma_strcpy_s_WCHAR(WCHAR* dst, size_t dstCap, const WCHAR* src)
19851{
19852 size_t i;
19853
19854 if (dst == 0) {
19855 return 22;
19856 }
19857 if (dstCap == 0) {
19858 return 34;
19859 }
19860 if (src == 0) {
19861 dst[0] = '\0';
19862 return 22;
19863 }
19864
19865 for (i = 0; i < dstCap && src[i] != '\0'; ++i) {
19866 dst[i] = src[i];
19867 }
19868
19869 if (i < dstCap) {
19870 dst[i] = '\0';
19871 return 0;
19872 }
19873
19874 dst[0] = '\0';
19875 return 34;
19876}
19877#endif /* MA_WIN32 */
19878
19879
19880#define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device"
19881#define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device"
19882
19883
19884
19885
19886/*******************************************************************************
19887
19888Timing
19889
19890*******************************************************************************/
19891#if defined(MA_WIN32) && !defined(MA_POSIX)
19892 static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */
19893 static void ma_timer_init(ma_timer* pTimer)
19894 {
19895 LARGE_INTEGER counter;
19896
19897 if (g_ma_TimerFrequency.QuadPart == 0) {
19898 QueryPerformanceFrequency(&g_ma_TimerFrequency);
19899 }
19900
19901 QueryPerformanceCounter(&counter);
19902 pTimer->counter = counter.QuadPart;
19903 }
19904
19905 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
19906 {
19907 LARGE_INTEGER counter;
19908 if (!QueryPerformanceCounter(&counter)) {
19909 return 0;
19910 }
19911
19912 return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;
19913 }
19914#elif defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
19915 static ma_uint64 g_ma_TimerFrequency = 0;
19916 static void ma_timer_init(ma_timer* pTimer)
19917 {
19918 mach_timebase_info_data_t baseTime;
19919 mach_timebase_info(&baseTime);
19920 g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;
19921
19922 pTimer->counter = mach_absolute_time();
19923 }
19924
19925 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
19926 {
19927 ma_uint64 newTimeCounter = mach_absolute_time();
19928 ma_uint64 oldTimeCounter = pTimer->counter;
19929
19930 return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;
19931 }
19932#elif defined(MA_EMSCRIPTEN)
19933 static MA_INLINE void ma_timer_init(ma_timer* pTimer)
19934 {
19935 pTimer->counterD = emscripten_get_now();
19936 }
19937
19938 static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
19939 {
19940 return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */
19941 }
19942#else
19943 #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L
19944 #if defined(CLOCK_MONOTONIC)
19945 #define MA_CLOCK_ID CLOCK_MONOTONIC
19946 #else
19947 #define MA_CLOCK_ID CLOCK_REALTIME
19948 #endif
19949
19950 static void ma_timer_init(ma_timer* pTimer)
19951 {
19952 struct timespec newTime;
19953 clock_gettime(MA_CLOCK_ID, &newTime);
19954
19955 pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
19956 }
19957
19958 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
19959 {
19960 ma_uint64 newTimeCounter;
19961 ma_uint64 oldTimeCounter;
19962
19963 struct timespec newTime;
19964 clock_gettime(MA_CLOCK_ID, &newTime);
19965
19966 newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
19967 oldTimeCounter = pTimer->counter;
19968
19969 return (newTimeCounter - oldTimeCounter) / 1000000000.0;
19970 }
19971 #else
19972 static void ma_timer_init(ma_timer* pTimer)
19973 {
19974 struct timeval newTime;
19975 gettimeofday(&newTime, NULL);
19976
19977 pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
19978 }
19979
19980 static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
19981 {
19982 ma_uint64 newTimeCounter;
19983 ma_uint64 oldTimeCounter;
19984
19985 struct timeval newTime;
19986 gettimeofday(&newTime, NULL);
19987
19988 newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
19989 oldTimeCounter = pTimer->counter;
19990
19991 return (newTimeCounter - oldTimeCounter) / 1000000.0;
19992 }
19993 #endif
19994#endif
19995
19996
19997
19998#if 0
19999static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn)
20000{
20001 ma_uint32 closestRate = 0;
20002 ma_uint32 closestDiff = 0xFFFFFFFF;
20003 size_t iStandardRate;
20004
20005 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
20006 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
20007 ma_uint32 diff;
20008
20009 if (sampleRateIn > standardRate) {
20010 diff = sampleRateIn - standardRate;
20011 } else {
20012 diff = standardRate - sampleRateIn;
20013 }
20014
20015 if (diff == 0) {
20016 return standardRate; /* The input sample rate is a standard rate. */
20017 }
20018
20019 if (closestDiff > diff) {
20020 closestDiff = diff;
20021 closestRate = standardRate;
20022 }
20023 }
20024
20025 return closestRate;
20026}
20027#endif
20028
20029
20030static MA_INLINE unsigned int ma_device_disable_denormals(ma_device* pDevice)
20031{
20032 MA_ASSERT(pDevice != NULL);
20033
20034 if (!pDevice->noDisableDenormals) {
20035 return ma_disable_denormals();
20036 } else {
20037 return 0;
20038 }
20039}
20040
20041static MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned int prevState)
20042{
20043 MA_ASSERT(pDevice != NULL);
20044
20045 if (!pDevice->noDisableDenormals) {
20046 ma_restore_denormals(prevState);
20047 } else {
20048 /* Do nothing. */
20049 (void)prevState;
20050 }
20051}
20052
20053static ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type)
20054{
20055 ma_device_notification notification;
20056
20057 MA_ZERO_OBJECT(&notification);
20058 notification.pDevice = pDevice;
20059 notification.type = type;
20060
20061 return notification;
20062}
20063
20064static void ma_device__on_notification(ma_device_notification notification)
20065{
20066 MA_ASSERT(notification.pDevice != NULL);
20067
20068 if (notification.pDevice->onNotification != NULL) {
20069 notification.pDevice->onNotification(&notification);
20070 }
20071
20072 /* 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. */
20073 if (notification.pDevice->onStop != NULL && notification.type == ma_device_notification_type_stopped) {
20074 notification.pDevice->onStop(notification.pDevice);
20075 }
20076}
20077
20078static void ma_device__on_notification_started(ma_device* pDevice)
20079{
20080 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started));
20081}
20082
20083static void ma_device__on_notification_stopped(ma_device* pDevice)
20084{
20085 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped));
20086}
20087
20088/* Not all platforms support reroute notifications. */
20089#if !defined(MA_EMSCRIPTEN)
20090static void ma_device__on_notification_rerouted(ma_device* pDevice)
20091{
20092 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted));
20093}
20094#endif
20095
20096#if defined(MA_EMSCRIPTEN)
20097#ifdef __cplusplus
20098extern "C" {
20099#endif
20100void EMSCRIPTEN_KEEPALIVE ma_device__on_notification_unlocked(ma_device* pDevice)
20101{
20102 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked));
20103}
20104#ifdef __cplusplus
20105}
20106#endif
20107#endif
20108
20109
20110static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
20111{
20112 MA_ASSERT(pDevice != NULL);
20113 MA_ASSERT(pDevice->onData != NULL);
20114
20115 if (!pDevice->noPreSilencedOutputBuffer && pFramesOut != NULL) {
20116 ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels);
20117 }
20118
20119 pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount);
20120}
20121
20122static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
20123{
20124 MA_ASSERT(pDevice != NULL);
20125
20126 /* Don't read more data from the client if we're in the process of stopping. */
20128 return;
20129 }
20130
20131 if (pDevice->noFixedSizedCallback) {
20132 /* Fast path. Not using a fixed sized callback. Process directly from the specified buffers. */
20133 ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount);
20134 } else {
20135 /* Slow path. Using a fixed sized callback. Need to use the intermediary buffer. */
20136 ma_uint32 totalFramesProcessed = 0;
20137
20138 while (totalFramesProcessed < frameCount) {
20139 ma_uint32 totalFramesRemaining = frameCount - totalFramesProcessed;
20140 ma_uint32 framesToProcessThisIteration = 0;
20141
20142 if (pFramesIn != NULL) {
20143 /* Capturing. Write to the intermediary buffer. If there's no room, fire the callback to empty it. */
20145 /* There's some room left in the intermediary buffer. Write to it without firing the callback. */
20146 framesToProcessThisIteration = totalFramesRemaining;
20147 if (framesToProcessThisIteration > pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen) {
20148 framesToProcessThisIteration = pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen;
20149 }
20150
20153 ma_offset_pcm_frames_const_ptr(pFramesIn, totalFramesProcessed, pDevice->capture.format, pDevice->capture.channels),
20154 framesToProcessThisIteration,
20155 pDevice->capture.format, pDevice->capture.channels);
20156
20157 pDevice->capture.intermediaryBufferLen += framesToProcessThisIteration;
20158 }
20159
20161 /* No room left in the intermediary buffer. Fire the data callback. */
20162 if (pDevice->type == ma_device_type_duplex) {
20163 /* We'll do the duplex data callback later after we've processed the playback data. */
20164 } else {
20165 ma_device__on_data_inner(pDevice, NULL, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap);
20166
20167 /* The intermediary buffer has just been drained. */
20168 pDevice->capture.intermediaryBufferLen = 0;
20169 }
20170 }
20171 }
20172
20173 if (pFramesOut != NULL) {
20174 /* Playing back. Read from the intermediary buffer. If there's nothing in it, fire the callback to fill it. */
20175 if (pDevice->playback.intermediaryBufferLen > 0) {
20176 /* There's some content in the intermediary buffer. Read from that without firing the callback. */
20177 if (pDevice->type == ma_device_type_duplex) {
20178 /* The frames processed this iteration for a duplex device will always be based on the capture side. Leave it unmodified. */
20179 } else {
20180 framesToProcessThisIteration = totalFramesRemaining;
20181 if (framesToProcessThisIteration > pDevice->playback.intermediaryBufferLen) {
20182 framesToProcessThisIteration = pDevice->playback.intermediaryBufferLen;
20183 }
20184 }
20185
20187 ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, pDevice->playback.format, pDevice->playback.channels),
20189 framesToProcessThisIteration,
20190 pDevice->playback.format, pDevice->playback.channels);
20191
20192 pDevice->playback.intermediaryBufferLen -= framesToProcessThisIteration;
20193 }
20194
20195 if (pDevice->playback.intermediaryBufferLen == 0) {
20196 /* There's nothing in the intermediary buffer. Fire the data callback to fill it. */
20197 if (pDevice->type == ma_device_type_duplex) {
20198 /* In duplex mode, the data callback will be fired later. Nothing to do here. */
20199 } else {
20200 ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, NULL, pDevice->playback.intermediaryBufferCap);
20201
20202 /* The intermediary buffer has just been filled. */
20204 }
20205 }
20206 }
20207
20208 /* If we're in duplex mode we might need to do a refill of the data. */
20209 if (pDevice->type == ma_device_type_duplex) {
20211 ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap);
20212
20213 pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; /* The playback buffer will have just been filled. */
20214 pDevice->capture.intermediaryBufferLen = 0; /* The intermediary buffer has just been drained. */
20215 }
20216 }
20217
20218 /* Make sure this is only incremented once in the duplex case. */
20219 totalFramesProcessed += framesToProcessThisIteration;
20220 }
20221 }
20222}
20223
20224static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
20225{
20226 float masterVolumeFactor;
20227
20228 ma_device_get_master_volume(pDevice, &masterVolumeFactor); /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */
20229
20230 if (pDevice->onData) {
20231 unsigned int prevDenormalState = ma_device_disable_denormals(pDevice);
20232 {
20233 /* 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. */
20234 if (pFramesIn != NULL && masterVolumeFactor != 1) {
20235 ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
20236 ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
20237 ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
20238 ma_uint32 totalFramesProcessed = 0;
20239 while (totalFramesProcessed < frameCount) {
20240 ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed;
20241 if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) {
20242 framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture;
20243 }
20244
20245 ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor);
20246
20247 ma_device__on_data(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration);
20248
20249 totalFramesProcessed += framesToProcessThisIteration;
20250 }
20251 } else {
20252 ma_device__on_data(pDevice, pFramesOut, pFramesIn, frameCount);
20253 }
20254
20255 /* Volume control and clipping for playback devices. */
20256 if (pFramesOut != NULL) {
20257 if (masterVolumeFactor != 1) {
20258 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. */
20259 ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor);
20260 }
20261 }
20262
20263 if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) {
20264 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. */
20265 }
20266 }
20267 }
20268 ma_device_restore_denormals(pDevice, prevDenormalState);
20269 } else {
20270 /* No data callback. Just silence the output. */
20271 if (pFramesOut != NULL) {
20272 ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels);
20273 }
20274 }
20275}
20276
20277
20278
20279/* A helper function for reading sample data from the client. */
20280static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut)
20281{
20282 MA_ASSERT(pDevice != NULL);
20283 MA_ASSERT(frameCount > 0);
20284 MA_ASSERT(pFramesOut != NULL);
20285
20286 if (pDevice->playback.converter.isPassthrough) {
20287 ma_device__handle_data_callback(pDevice, pFramesOut, NULL, frameCount);
20288 } else {
20289 ma_result result;
20290 ma_uint64 totalFramesReadOut;
20291 void* pRunningFramesOut;
20292
20293 totalFramesReadOut = 0;
20294 pRunningFramesOut = pFramesOut;
20295
20296 /*
20297 We run slightly different logic depending on whether or not we're using a heap-allocated
20298 buffer for caching input data. This will be the case if the data converter does not have
20299 the ability to retrieve the required input frame count for a given output frame count.
20300 */
20301 if (pDevice->playback.pInputCache != NULL) {
20302 while (totalFramesReadOut < frameCount) {
20303 ma_uint64 framesToReadThisIterationIn;
20304 ma_uint64 framesToReadThisIterationOut;
20305
20306 /* If there's any data available in the cache, that needs to get processed first. */
20307 if (pDevice->playback.inputCacheRemaining > 0) {
20308 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
20309 framesToReadThisIterationIn = framesToReadThisIterationOut;
20310 if (framesToReadThisIterationIn > pDevice->playback.inputCacheRemaining) {
20311 framesToReadThisIterationIn = pDevice->playback.inputCacheRemaining;
20312 }
20313
20314 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);
20315 if (result != MA_SUCCESS) {
20316 break;
20317 }
20318
20319 pDevice->playback.inputCacheConsumed += framesToReadThisIterationIn;
20320 pDevice->playback.inputCacheRemaining -= framesToReadThisIterationIn;
20321
20322 totalFramesReadOut += framesToReadThisIterationOut;
20323 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
20324
20325 if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) {
20326 break; /* We're done. */
20327 }
20328 }
20329
20330 /* Getting here means there's no data in the cache and we need to fill it up with data from the client. */
20331 if (pDevice->playback.inputCacheRemaining == 0) {
20332 ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, NULL, (ma_uint32)pDevice->playback.inputCacheCap);
20333
20334 pDevice->playback.inputCacheConsumed = 0;
20336 }
20337 }
20338 } else {
20339 while (totalFramesReadOut < frameCount) {
20340 ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */
20341 ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
20342 ma_uint64 framesToReadThisIterationIn;
20343 ma_uint64 framesReadThisIterationIn;
20344 ma_uint64 framesToReadThisIterationOut;
20345 ma_uint64 framesReadThisIterationOut;
20346 ma_uint64 requiredInputFrameCount;
20347
20348 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
20349 framesToReadThisIterationIn = framesToReadThisIterationOut;
20350 if (framesToReadThisIterationIn > intermediaryBufferCap) {
20351 framesToReadThisIterationIn = intermediaryBufferCap;
20352 }
20353
20354 ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut, &requiredInputFrameCount);
20355 if (framesToReadThisIterationIn > requiredInputFrameCount) {
20356 framesToReadThisIterationIn = requiredInputFrameCount;
20357 }
20358
20359 ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn);
20360
20361 /*
20362 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
20363 input frames, we still want to try processing frames because there may some output frames generated from cached input data.
20364 */
20365 framesReadThisIterationIn = framesToReadThisIterationIn;
20366 framesReadThisIterationOut = framesToReadThisIterationOut;
20367 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
20368 if (result != MA_SUCCESS) {
20369 break;
20370 }
20371
20372 totalFramesReadOut += framesReadThisIterationOut;
20373 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
20374
20375 if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
20376 break; /* We're done. */
20377 }
20378 }
20379 }
20380 }
20381}
20382
20383/* A helper for sending sample data to the client. */
20384static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat)
20385{
20386 MA_ASSERT(pDevice != NULL);
20387 MA_ASSERT(frameCountInDeviceFormat > 0);
20388 MA_ASSERT(pFramesInDeviceFormat != NULL);
20389
20390 if (pDevice->capture.converter.isPassthrough) {
20391 ma_device__handle_data_callback(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat);
20392 } else {
20393 ma_result result;
20394 ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
20395 ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
20396 ma_uint64 totalDeviceFramesProcessed = 0;
20397 ma_uint64 totalClientFramesProcessed = 0;
20398 const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
20399
20400 /* We just keep going until we've exhausted all of our input frames and cannot generate any more output frames. */
20401 for (;;) {
20402 ma_uint64 deviceFramesProcessedThisIteration;
20403 ma_uint64 clientFramesProcessedThisIteration;
20404
20405 deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
20406 clientFramesProcessedThisIteration = framesInClientFormatCap;
20407
20408 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration);
20409 if (result != MA_SUCCESS) {
20410 break;
20411 }
20412
20413 if (clientFramesProcessedThisIteration > 0) {
20414 ma_device__handle_data_callback(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */
20415 }
20416
20417 pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
20418 totalDeviceFramesProcessed += deviceFramesProcessedThisIteration;
20419 totalClientFramesProcessed += clientFramesProcessedThisIteration;
20420
20421 /* This is just to silence a warning. I might want to use this variable later so leaving in place for now. */
20422 (void)totalClientFramesProcessed;
20423
20424 if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) {
20425 break; /* We're done. */
20426 }
20427 }
20428 }
20429}
20430
20431static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB)
20432{
20433 ma_result result;
20434 ma_uint32 totalDeviceFramesProcessed = 0;
20435 const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
20436
20437 MA_ASSERT(pDevice != NULL);
20438 MA_ASSERT(frameCountInDeviceFormat > 0);
20439 MA_ASSERT(pFramesInDeviceFormat != NULL);
20440 MA_ASSERT(pRB != NULL);
20441
20442 /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */
20443 for (;;) {
20444 ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
20445 ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
20446 ma_uint64 framesProcessedInDeviceFormat;
20447 ma_uint64 framesProcessedInClientFormat;
20448 void* pFramesInClientFormat;
20449
20450 result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat);
20451 if (result != MA_SUCCESS) {
20452 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer.");
20453 break;
20454 }
20455
20456 if (framesToProcessInClientFormat == 0) {
20458 break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */
20459 }
20460 }
20461
20462 /* Convert. */
20463 framesProcessedInDeviceFormat = framesToProcessInDeviceFormat;
20464 framesProcessedInClientFormat = framesToProcessInClientFormat;
20465 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat);
20466 if (result != MA_SUCCESS) {
20467 break;
20468 }
20469
20470 result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat); /* Safe cast. */
20471 if (result != MA_SUCCESS) {
20472 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer.");
20473 break;
20474 }
20475
20476 pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
20477 totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */
20478
20479 /* We're done when we're unable to process any client nor device frames. */
20480 if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) {
20481 break; /* Done. */
20482 }
20483 }
20484
20485 return MA_SUCCESS;
20486}
20487
20488static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB)
20489{
20490 ma_result result;
20491 ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
20492 ma_uint32 totalFramesReadOut = 0;
20493
20494 MA_ASSERT(pDevice != NULL);
20495 MA_ASSERT(frameCount > 0);
20496 MA_ASSERT(pFramesInInternalFormat != NULL);
20497 MA_ASSERT(pRB != NULL);
20498 MA_ASSERT(pDevice->playback.pInputCache != NULL);
20499
20500 /*
20501 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
20502 the whole frameCount frames we just use silence instead for the input data.
20503 */
20504 MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames));
20505
20506 while (totalFramesReadOut < frameCount && ma_device_is_started(pDevice)) {
20507 /*
20508 We should have a buffer allocated on the heap. Any playback frames still sitting in there
20509 need to be sent to the internal device before we process any more data from the client.
20510 */
20511 if (pDevice->playback.inputCacheRemaining > 0) {
20512 ma_uint64 framesConvertedIn = pDevice->playback.inputCacheRemaining;
20513 ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut);
20514 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);
20515
20516 pDevice->playback.inputCacheConsumed += framesConvertedIn;
20517 pDevice->playback.inputCacheRemaining -= framesConvertedIn;
20518
20519 totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */
20520 pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
20521 }
20522
20523 /* If there's no more data in the cache we'll need to fill it with some. */
20524 if (totalFramesReadOut < frameCount && pDevice->playback.inputCacheRemaining == 0) {
20525 ma_uint32 inputFrameCount;
20526 void* pInputFrames;
20527
20528 inputFrameCount = (ma_uint32)pDevice->playback.inputCacheCap;
20529 result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames);
20530 if (result == MA_SUCCESS) {
20531 if (inputFrameCount > 0) {
20532 ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount);
20533 } else {
20534 if (ma_pcm_rb_pointer_distance(pRB) == 0) {
20535 break; /* Underrun. */
20536 }
20537 }
20538 } else {
20539 /* No capture data available. Feed in silence. */
20540 inputFrameCount = (ma_uint32)ma_min(pDevice->playback.inputCacheCap, sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
20541 ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, silentInputFrames, inputFrameCount);
20542 }
20543
20544 pDevice->playback.inputCacheConsumed = 0;
20545 pDevice->playback.inputCacheRemaining = inputFrameCount;
20546
20547 result = ma_pcm_rb_commit_read(pRB, inputFrameCount);
20548 if (result != MA_SUCCESS) {
20549 return result; /* Should never happen. */
20550 }
20551 }
20552 }
20553
20554 return MA_SUCCESS;
20555}
20556
20557/* A helper for changing the state of the device. */
20558static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState)
20559{
20560 ma_atomic_device_state_set(&pDevice->state, newState);
20561}
20562
20563
20564#if defined(MA_WIN32)
20565 static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
20566 static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
20567 /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
20568 /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
20569#endif
20570
20571
20572
20573MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */
20574{
20575 ma_uint32 i;
20576 for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) {
20577 if (g_maFormatPriorities[i] == format) {
20578 return i;
20579 }
20580 }
20581
20582 /* Getting here means the format could not be found or is equal to ma_format_unknown. */
20583 return (ma_uint32)-1;
20584}
20585
20586static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType);
20587
20588static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor)
20589{
20590 if (pDeviceDescriptor == NULL) {
20591 return MA_FALSE;
20592 }
20593
20594 if (pDeviceDescriptor->format == ma_format_unknown) {
20595 return MA_FALSE;
20596 }
20597
20598 if (pDeviceDescriptor->channels == 0 || pDeviceDescriptor->channels > MA_MAX_CHANNELS) {
20599 return MA_FALSE;
20600 }
20601
20602 if (pDeviceDescriptor->sampleRate == 0) {
20603 return MA_FALSE;
20604 }
20605
20606 return MA_TRUE;
20607}
20608
20609
20610static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice)
20611{
20612 ma_result result = MA_SUCCESS;
20613 ma_bool32 exitLoop = MA_FALSE;
20614 ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
20615 ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
20616 ma_uint32 capturedDeviceDataCapInFrames = 0;
20617 ma_uint32 playbackDeviceDataCapInFrames = 0;
20618
20619 MA_ASSERT(pDevice != NULL);
20620
20621 /* Just some quick validation on the device type and the available callbacks. */
20622 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
20623 if (pDevice->pContext->callbacks.onDeviceRead == NULL) {
20624 return MA_NOT_IMPLEMENTED;
20625 }
20626
20627 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
20628 }
20629
20630 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
20631 if (pDevice->pContext->callbacks.onDeviceWrite == NULL) {
20632 return MA_NOT_IMPLEMENTED;
20633 }
20634
20635 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
20636 }
20637
20638 /* NOTE: The device was started outside of this function, in the worker thread. */
20639
20640 while (ma_device_get_state(pDevice) == ma_device_state_started && !exitLoop) {
20641 switch (pDevice->type) {
20643 {
20644 /* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */
20645 ma_uint32 totalCapturedDeviceFramesProcessed = 0;
20646 ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
20647
20648 while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
20649 ma_uint32 capturedDeviceFramesRemaining;
20650 ma_uint32 capturedDeviceFramesProcessed;
20651 ma_uint32 capturedDeviceFramesToProcess;
20652 ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
20653 if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
20654 capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
20655 }
20656
20657 result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
20658 if (result != MA_SUCCESS) {
20659 exitLoop = MA_TRUE;
20660 break;
20661 }
20662
20663 capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
20664 capturedDeviceFramesProcessed = 0;
20665
20666 /* At this point we have our captured data in device format and we now need to convert it to client format. */
20667 for (;;) {
20668 ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
20669 ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
20670 ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
20671 ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
20672 ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
20673 ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
20674 ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
20675
20676 /* Convert capture data from device format to client format. */
20677 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
20678 if (result != MA_SUCCESS) {
20679 break;
20680 }
20681
20682 /*
20683 If we weren't able to generate any output frames it must mean we've exhausted all of our input. The only time this would not be the case is if capturedClientData was too small
20684 which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE.
20685 */
20686 if (capturedClientFramesToProcessThisIteration == 0) {
20687 break;
20688 }
20689
20690 ma_device__handle_data_callback(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/
20691
20692 capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
20693 capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */
20694
20695 /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */
20696 for (;;) {
20697 ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
20698 ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
20699 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
20700 if (result != MA_SUCCESS) {
20701 break;
20702 }
20703
20704 result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */
20705 if (result != MA_SUCCESS) {
20706 exitLoop = MA_TRUE;
20707 break;
20708 }
20709
20710 capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */
20711 if (capturedClientFramesToProcessThisIteration == 0) {
20712 break;
20713 }
20714 }
20715
20716 /* In case an error happened from ma_device_write__null()... */
20717 if (result != MA_SUCCESS) {
20718 exitLoop = MA_TRUE;
20719 break;
20720 }
20721 }
20722
20723 /* Make sure we don't get stuck in the inner loop. */
20724 if (capturedDeviceFramesProcessed == 0) {
20725 break;
20726 }
20727
20728 totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
20729 }
20730 } break;
20731
20734 {
20735 ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
20736 ma_uint32 framesReadThisPeriod = 0;
20737 while (framesReadThisPeriod < periodSizeInFrames) {
20738 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
20739 ma_uint32 framesProcessed;
20740 ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
20741 if (framesToReadThisIteration > capturedDeviceDataCapInFrames) {
20742 framesToReadThisIteration = capturedDeviceDataCapInFrames;
20743 }
20744
20745 result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed);
20746 if (result != MA_SUCCESS) {
20747 exitLoop = MA_TRUE;
20748 break;
20749 }
20750
20751 /* Make sure we don't get stuck in the inner loop. */
20752 if (framesProcessed == 0) {
20753 break;
20754 }
20755
20756 ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData);
20757
20758 framesReadThisPeriod += framesProcessed;
20759 }
20760 } break;
20761
20763 {
20764 /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
20765 ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
20766 ma_uint32 framesWrittenThisPeriod = 0;
20767 while (framesWrittenThisPeriod < periodSizeInFrames) {
20768 ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
20769 ma_uint32 framesProcessed;
20770 ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
20771 if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) {
20772 framesToWriteThisIteration = playbackDeviceDataCapInFrames;
20773 }
20774
20775 ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData);
20776
20777 result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed);
20778 if (result != MA_SUCCESS) {
20779 exitLoop = MA_TRUE;
20780 break;
20781 }
20782
20783 /* Make sure we don't get stuck in the inner loop. */
20784 if (framesProcessed == 0) {
20785 break;
20786 }
20787
20788 framesWrittenThisPeriod += framesProcessed;
20789 }
20790 } break;
20791
20792 /* Should never get here. */
20793 default: break;
20794 }
20795 }
20796
20797 return result;
20798}
20799
20800
20801
20802/*******************************************************************************
20803
20804Null Backend
20805
20806*******************************************************************************/
20807#ifdef MA_HAS_NULL
20808
20809#define MA_DEVICE_OP_NONE__NULL 0
20810#define MA_DEVICE_OP_START__NULL 1
20811#define MA_DEVICE_OP_SUSPEND__NULL 2
20812#define MA_DEVICE_OP_KILL__NULL 3
20813
20814static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData)
20815{
20816 ma_device* pDevice = (ma_device*)pData;
20817 MA_ASSERT(pDevice != NULL);
20818
20819 for (;;) { /* Keep the thread alive until the device is uninitialized. */
20820 ma_uint32 operation;
20821
20822 /* Wait for an operation to be requested. */
20824
20825 /* At this point an event should have been triggered. */
20826 operation = pDevice->null_device.operation;
20827
20828 /* Starting the device needs to put the thread into a loop. */
20829 if (operation == MA_DEVICE_OP_START__NULL) {
20830 /* Reset the timer just in case. */
20831 ma_timer_init(&pDevice->null_device.timer);
20832
20833 /* Getting here means a suspend or kill operation has been requested. */
20837 continue;
20838 }
20839
20840 /* Suspending the device means we need to stop the timer and just continue the loop. */
20841 if (operation == MA_DEVICE_OP_SUSPEND__NULL) {
20842 /* We need to add the current run time to the prior run time, then reset the timer. */
20843 pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);
20844 ma_timer_init(&pDevice->null_device.timer);
20845
20846 /* We're done. */
20850 continue;
20851 }
20852
20853 /* Killing the device means we need to get out of this loop so that this thread can terminate. */
20854 if (operation == MA_DEVICE_OP_KILL__NULL) {
20858 break;
20859 }
20860
20861 /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */
20862 if (operation == MA_DEVICE_OP_NONE__NULL) {
20863 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). */
20867 continue; /* Continue the loop. Don't terminate. */
20868 }
20869 }
20870
20871 return (ma_thread_result)0;
20872}
20873
20874static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation)
20875{
20876 ma_result result;
20877
20878 /*
20879 TODO: Need to review this and consider just using mutual exclusion. I think the original motivation
20880 for this was to just post the event to a queue and return immediately, but that has since changed
20881 and now this function is synchronous. I think this can be simplified to just use a mutex.
20882 */
20883
20884 /*
20885 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
20886 to support queuing of operations.
20887 */
20889 if (result != MA_SUCCESS) {
20890 return result; /* Failed to wait for the event. */
20891 }
20892
20893 /*
20894 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
20895 signal an event to the worker thread to let it know that it can start work.
20896 */
20897 pDevice->null_device.operation = operation;
20898
20899 /* Once the operation code has been set, the worker thread can start work. */
20901 return MA_ERROR;
20902 }
20903
20904 /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */
20906 return MA_ERROR;
20907 }
20908
20909 return pDevice->null_device.operationResult;
20910}
20911
20912static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)
20913{
20914 ma_uint32 internalSampleRate;
20915 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
20916 internalSampleRate = pDevice->capture.internalSampleRate;
20917 } else {
20918 internalSampleRate = pDevice->playback.internalSampleRate;
20919 }
20920
20921 return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);
20922}
20923
20924static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
20925{
20926 ma_bool32 cbResult = MA_TRUE;
20927
20928 MA_ASSERT(pContext != NULL);
20929 MA_ASSERT(callback != NULL);
20930
20931 /* Playback. */
20932 if (cbResult) {
20933 ma_device_info deviceInfo;
20934 MA_ZERO_OBJECT(&deviceInfo);
20935 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1);
20936 deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
20937 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
20938 }
20939
20940 /* Capture. */
20941 if (cbResult) {
20942 ma_device_info deviceInfo;
20943 MA_ZERO_OBJECT(&deviceInfo);
20944 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1);
20945 deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
20946 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
20947 }
20948
20949 (void)cbResult; /* Silence a static analysis warning. */
20950
20951 return MA_SUCCESS;
20952}
20953
20954static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
20955{
20956 MA_ASSERT(pContext != NULL);
20957
20958 if (pDeviceID != NULL && pDeviceID->nullbackend != 0) {
20959 return MA_NO_DEVICE; /* Don't know the device. */
20960 }
20961
20962 /* Name / Description */
20963 if (deviceType == ma_device_type_playback) {
20964 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1);
20965 } else {
20966 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1);
20967 }
20968
20969 pDeviceInfo->isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */
20970
20971 /* Support everything on the null backend. */
20972 pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown;
20973 pDeviceInfo->nativeDataFormats[0].channels = 0;
20974 pDeviceInfo->nativeDataFormats[0].sampleRate = 0;
20975 pDeviceInfo->nativeDataFormats[0].flags = 0;
20976 pDeviceInfo->nativeDataFormatCount = 1;
20977
20978 (void)pContext;
20979 return MA_SUCCESS;
20980}
20981
20982
20983static ma_result ma_device_uninit__null(ma_device* pDevice)
20984{
20985 MA_ASSERT(pDevice != NULL);
20986
20987 /* Keep it clean and wait for the device thread to finish before returning. */
20988 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL);
20989
20990 /* Wait for the thread to finish before continuing. */
20991 ma_thread_wait(&pDevice->null_device.deviceThread);
20992
20993 /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */
20997
20998 return MA_SUCCESS;
20999}
21000
21001static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
21002{
21003 ma_result result;
21004
21005 MA_ASSERT(pDevice != NULL);
21006
21007 MA_ZERO_OBJECT(&pDevice->null_device);
21008
21009 if (pConfig->deviceType == ma_device_type_loopback) {
21011 }
21012
21013 /* The null backend supports everything exactly as we specify it. */
21014 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
21015 pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT;
21016 pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS;
21017 pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE;
21018
21019 if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) {
21020 ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
21021 }
21022
21023 pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
21024 }
21025
21026 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
21027 pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT;
21028 pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS;
21029 pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE;
21030
21031 if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) {
21032 ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels);
21033 }
21034
21035 pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
21036 }
21037
21038 /*
21039 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
21040 first period is "written" to it, and then stopped in ma_device_stop__null().
21041 */
21042 result = ma_event_init(&pDevice->null_device.operationEvent);
21043 if (result != MA_SUCCESS) {
21044 return result;
21045 }
21046
21048 if (result != MA_SUCCESS) {
21049 return result;
21050 }
21051
21052 result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */
21053 if (result != MA_SUCCESS) {
21054 return result;
21055 }
21056
21057 result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks);
21058 if (result != MA_SUCCESS) {
21059 return result;
21060 }
21061
21062 return MA_SUCCESS;
21063}
21064
21065static ma_result ma_device_start__null(ma_device* pDevice)
21066{
21067 MA_ASSERT(pDevice != NULL);
21068
21069 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL);
21070
21071 ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_TRUE);
21072 return MA_SUCCESS;
21073}
21074
21075static ma_result ma_device_stop__null(ma_device* pDevice)
21076{
21077 MA_ASSERT(pDevice != NULL);
21078
21079 ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL);
21080
21081 ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_FALSE);
21082 return MA_SUCCESS;
21083}
21084
21085static ma_bool32 ma_device_is_started__null(ma_device* pDevice)
21086{
21087 MA_ASSERT(pDevice != NULL);
21088
21089 return ma_atomic_bool32_get(&pDevice->null_device.isStarted);
21090}
21091
21092static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
21093{
21094 ma_result result = MA_SUCCESS;
21095 ma_uint32 totalPCMFramesProcessed;
21096 ma_bool32 wasStartedOnEntry;
21097
21098 if (pFramesWritten != NULL) {
21099 *pFramesWritten = 0;
21100 }
21101
21102 wasStartedOnEntry = ma_device_is_started__null(pDevice);
21103
21104 /* Keep going until everything has been read. */
21105 totalPCMFramesProcessed = 0;
21106 while (totalPCMFramesProcessed < frameCount) {
21107 ma_uint64 targetFrame;
21108
21109 /* If there are any frames remaining in the current period, consume those first. */
21111 ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
21113 if (framesToProcess > framesRemaining) {
21114 framesToProcess = framesRemaining;
21115 }
21116
21117 /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */
21118 (void)pPCMFrames;
21119
21120 pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess;
21121 totalPCMFramesProcessed += framesToProcess;
21122 }
21123
21124 /* 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. */
21127
21128 if (!ma_device_is_started__null(pDevice) && !wasStartedOnEntry) {
21129 result = ma_device_start__null(pDevice);
21130 if (result != MA_SUCCESS) {
21131 break;
21132 }
21133 }
21134 }
21135
21136 /* If we've consumed the whole buffer we can return now. */
21137 MA_ASSERT(totalPCMFramesProcessed <= frameCount);
21138 if (totalPCMFramesProcessed == frameCount) {
21139 break;
21140 }
21141
21142 /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
21143 targetFrame = pDevice->null_device.lastProcessedFramePlayback;
21144 for (;;) {
21145 ma_uint64 currentFrame;
21146
21147 /* Stop waiting if the device has been stopped. */
21148 if (!ma_device_is_started__null(pDevice)) {
21149 break;
21150 }
21151
21152 currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
21153 if (currentFrame >= targetFrame) {
21154 break;
21155 }
21156
21157 /* Getting here means we haven't yet reached the target sample, so continue waiting. */
21158 ma_sleep(10);
21159 }
21160
21163 }
21164
21165 if (pFramesWritten != NULL) {
21166 *pFramesWritten = totalPCMFramesProcessed;
21167 }
21168
21169 return result;
21170}
21171
21172static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
21173{
21174 ma_result result = MA_SUCCESS;
21175 ma_uint32 totalPCMFramesProcessed;
21176
21177 if (pFramesRead != NULL) {
21178 *pFramesRead = 0;
21179 }
21180
21181 /* Keep going until everything has been read. */
21182 totalPCMFramesProcessed = 0;
21183 while (totalPCMFramesProcessed < frameCount) {
21184 ma_uint64 targetFrame;
21185
21186 /* If there are any frames remaining in the current period, consume those first. */
21189 ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
21191 if (framesToProcess > framesRemaining) {
21192 framesToProcess = framesRemaining;
21193 }
21194
21195 /* We need to ensure the output buffer is zeroed. */
21196 MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf);
21197
21198 pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess;
21199 totalPCMFramesProcessed += framesToProcess;
21200 }
21201
21202 /* 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. */
21205 }
21206
21207 /* If we've consumed the whole buffer we can return now. */
21208 MA_ASSERT(totalPCMFramesProcessed <= frameCount);
21209 if (totalPCMFramesProcessed == frameCount) {
21210 break;
21211 }
21212
21213 /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
21215 for (;;) {
21216 ma_uint64 currentFrame;
21217
21218 /* Stop waiting if the device has been stopped. */
21219 if (!ma_device_is_started__null(pDevice)) {
21220 break;
21221 }
21222
21223 currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
21224 if (currentFrame >= targetFrame) {
21225 break;
21226 }
21227
21228 /* Getting here means we haven't yet reached the target sample, so continue waiting. */
21229 ma_sleep(10);
21230 }
21231
21234 }
21235
21236 if (pFramesRead != NULL) {
21237 *pFramesRead = totalPCMFramesProcessed;
21238 }
21239
21240 return result;
21241}
21242
21243static ma_result ma_context_uninit__null(ma_context* pContext)
21244{
21245 MA_ASSERT(pContext != NULL);
21246 MA_ASSERT(pContext->backend == ma_backend_null);
21247
21248 (void)pContext;
21249 return MA_SUCCESS;
21250}
21251
21252static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
21253{
21254 MA_ASSERT(pContext != NULL);
21255
21256 (void)pConfig;
21257 (void)pContext;
21258
21259 pCallbacks->onContextInit = ma_context_init__null;
21260 pCallbacks->onContextUninit = ma_context_uninit__null;
21261 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null;
21262 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null;
21263 pCallbacks->onDeviceInit = ma_device_init__null;
21264 pCallbacks->onDeviceUninit = ma_device_uninit__null;
21265 pCallbacks->onDeviceStart = ma_device_start__null;
21266 pCallbacks->onDeviceStop = ma_device_stop__null;
21267 pCallbacks->onDeviceRead = ma_device_read__null;
21268 pCallbacks->onDeviceWrite = ma_device_write__null;
21269 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. */
21270
21271 /* The null backend always works. */
21272 return MA_SUCCESS;
21273}
21274#endif
21275
21276
21277
21278/*******************************************************************************
21279
21280WIN32 COMMON
21281
21282*******************************************************************************/
21283#if defined(MA_WIN32)
21284#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21285 #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved))
21286 #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)()
21287 #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv)
21288 #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv)
21289 #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar)
21290#else
21291 #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit)
21292 #define ma_CoUninitialize(pContext) CoUninitialize()
21293 #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv)
21294 #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv)
21295 #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar)
21296#endif
21297
21298#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__)
21299typedef size_t DWORD_PTR;
21300#endif
21301
21302#if !defined(WAVE_FORMAT_1M08)
21303#define WAVE_FORMAT_1M08 0x00000001
21304#define WAVE_FORMAT_1S08 0x00000002
21305#define WAVE_FORMAT_1M16 0x00000004
21306#define WAVE_FORMAT_1S16 0x00000008
21307#define WAVE_FORMAT_2M08 0x00000010
21308#define WAVE_FORMAT_2S08 0x00000020
21309#define WAVE_FORMAT_2M16 0x00000040
21310#define WAVE_FORMAT_2S16 0x00000080
21311#define WAVE_FORMAT_4M08 0x00000100
21312#define WAVE_FORMAT_4S08 0x00000200
21313#define WAVE_FORMAT_4M16 0x00000400
21314#define WAVE_FORMAT_4S16 0x00000800
21315#endif
21316
21317#if !defined(WAVE_FORMAT_44M08)
21318#define WAVE_FORMAT_44M08 0x00000100
21319#define WAVE_FORMAT_44S08 0x00000200
21320#define WAVE_FORMAT_44M16 0x00000400
21321#define WAVE_FORMAT_44S16 0x00000800
21322#define WAVE_FORMAT_48M08 0x00001000
21323#define WAVE_FORMAT_48S08 0x00002000
21324#define WAVE_FORMAT_48M16 0x00004000
21325#define WAVE_FORMAT_48S16 0x00008000
21326#define WAVE_FORMAT_96M08 0x00010000
21327#define WAVE_FORMAT_96S08 0x00020000
21328#define WAVE_FORMAT_96M16 0x00040000
21329#define WAVE_FORMAT_96S16 0x00080000
21330#endif
21331
21332#ifndef SPEAKER_FRONT_LEFT
21333#define SPEAKER_FRONT_LEFT 0x1
21334#define SPEAKER_FRONT_RIGHT 0x2
21335#define SPEAKER_FRONT_CENTER 0x4
21336#define SPEAKER_LOW_FREQUENCY 0x8
21337#define SPEAKER_BACK_LEFT 0x10
21338#define SPEAKER_BACK_RIGHT 0x20
21339#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
21340#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
21341#define SPEAKER_BACK_CENTER 0x100
21342#define SPEAKER_SIDE_LEFT 0x200
21343#define SPEAKER_SIDE_RIGHT 0x400
21344#define SPEAKER_TOP_CENTER 0x800
21345#define SPEAKER_TOP_FRONT_LEFT 0x1000
21346#define SPEAKER_TOP_FRONT_CENTER 0x2000
21347#define SPEAKER_TOP_FRONT_RIGHT 0x4000
21348#define SPEAKER_TOP_BACK_LEFT 0x8000
21349#define SPEAKER_TOP_BACK_CENTER 0x10000
21350#define SPEAKER_TOP_BACK_RIGHT 0x20000
21351#endif
21352
21353/*
21354Implement our own version of MA_WAVEFORMATEXTENSIBLE so we can avoid a header. Be careful with this
21355because MA_WAVEFORMATEX has an extra two bytes over standard WAVEFORMATEX due to padding. The
21356standard version uses tight packing, but for compiler compatibility we're not doing that with ours.
21357*/
21358typedef struct
21359{
21360 WORD wFormatTag;
21361 WORD nChannels;
21362 DWORD nSamplesPerSec;
21363 DWORD nAvgBytesPerSec;
21364 WORD nBlockAlign;
21365 WORD wBitsPerSample;
21366 WORD cbSize;
21367} MA_WAVEFORMATEX;
21368
21369typedef struct
21370{
21371 WORD wFormatTag;
21372 WORD nChannels;
21373 DWORD nSamplesPerSec;
21374 DWORD nAvgBytesPerSec;
21375 WORD nBlockAlign;
21376 WORD wBitsPerSample;
21377 WORD cbSize;
21378 union
21379 {
21380 WORD wValidBitsPerSample;
21381 WORD wSamplesPerBlock;
21382 WORD wReserved;
21383 } Samples;
21384 DWORD dwChannelMask;
21385 GUID SubFormat;
21386} MA_WAVEFORMATEXTENSIBLE;
21387
21388
21389
21390#ifndef WAVE_FORMAT_EXTENSIBLE
21391#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
21392#endif
21393
21394#ifndef WAVE_FORMAT_PCM
21395#define WAVE_FORMAT_PCM 1
21396#endif
21397
21398#ifndef WAVE_FORMAT_IEEE_FLOAT
21399#define WAVE_FORMAT_IEEE_FLOAT 0x0003
21400#endif
21401
21402/* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
21403static ma_uint8 ma_channel_id_to_ma__win32(DWORD id)
21404{
21405 switch (id)
21406 {
21407 case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
21408 case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
21409 case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
21410 case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
21411 case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
21412 case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
21413 case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
21414 case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
21415 case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
21416 case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
21417 case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
21418 case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
21419 case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
21420 case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
21421 case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
21422 case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
21423 case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
21424 case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
21425 default: return 0;
21426 }
21427}
21428
21429/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */
21430static DWORD ma_channel_id_to_win32(DWORD id)
21431{
21432 switch (id)
21433 {
21434 case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER;
21435 case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT;
21436 case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT;
21437 case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER;
21438 case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY;
21439 case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT;
21440 case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT;
21441 case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER;
21442 case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER;
21443 case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER;
21444 case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT;
21445 case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT;
21446 case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER;
21447 case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT;
21448 case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER;
21449 case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT;
21450 case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT;
21451 case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER;
21452 case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT;
21453 default: return 0;
21454 }
21455}
21456
21457/* Converts a channel mapping to a Win32-style channel mask. */
21458static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels)
21459{
21460 DWORD dwChannelMask = 0;
21461 ma_uint32 iChannel;
21462
21463 for (iChannel = 0; iChannel < channels; ++iChannel) {
21464 dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]);
21465 }
21466
21467 return dwChannelMask;
21468}
21469
21470/* Converts a Win32-style channel mask to a miniaudio channel map. */
21471static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap)
21472{
21473 /* If the channel mask is set to 0, just assume a default Win32 channel map. */
21474 if (dwChannelMask == 0) {
21475 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channels, channels);
21476 } else {
21477 if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) {
21478 pChannelMap[0] = MA_CHANNEL_MONO;
21479 } else {
21480 /* Just iterate over each bit. */
21481 ma_uint32 iChannel = 0;
21482 ma_uint32 iBit;
21483
21484 for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {
21485 DWORD bitValue = (dwChannelMask & (1UL << iBit));
21486 if (bitValue != 0) {
21487 /* The bit is set. */
21488 pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue);
21489 iChannel += 1;
21490 }
21491 }
21492 }
21493 }
21494}
21495
21496#ifdef __cplusplus
21497static ma_bool32 ma_is_guid_equal(const void* a, const void* b)
21498{
21499 return IsEqualGUID(*(const GUID*)a, *(const GUID*)b);
21500}
21501#else
21502#define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b)
21503#endif
21504
21505static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid)
21506{
21507 static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
21508 return ma_is_guid_equal(guid, &nullguid);
21509}
21510
21511static ma_format ma_format_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF)
21512{
21513 MA_ASSERT(pWF != NULL);
21514
21515 if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
21516 const MA_WAVEFORMATEXTENSIBLE* pWFEX = (const MA_WAVEFORMATEXTENSIBLE*)pWF;
21517 if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) {
21518 if (pWFEX->Samples.wValidBitsPerSample == 32) {
21519 return ma_format_s32;
21520 }
21521 if (pWFEX->Samples.wValidBitsPerSample == 24) {
21522 if (pWFEX->wBitsPerSample == 32) {
21523 return ma_format_s32;
21524 }
21525 if (pWFEX->wBitsPerSample == 24) {
21526 return ma_format_s24;
21527 }
21528 }
21529 if (pWFEX->Samples.wValidBitsPerSample == 16) {
21530 return ma_format_s16;
21531 }
21532 if (pWFEX->Samples.wValidBitsPerSample == 8) {
21533 return ma_format_u8;
21534 }
21535 }
21536 if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
21537 if (pWFEX->Samples.wValidBitsPerSample == 32) {
21538 return ma_format_f32;
21539 }
21540 /*
21541 if (pWFEX->Samples.wValidBitsPerSample == 64) {
21542 return ma_format_f64;
21543 }
21544 */
21545 }
21546 } else {
21547 if (pWF->wFormatTag == WAVE_FORMAT_PCM) {
21548 if (pWF->wBitsPerSample == 32) {
21549 return ma_format_s32;
21550 }
21551 if (pWF->wBitsPerSample == 24) {
21552 return ma_format_s24;
21553 }
21554 if (pWF->wBitsPerSample == 16) {
21555 return ma_format_s16;
21556 }
21557 if (pWF->wBitsPerSample == 8) {
21558 return ma_format_u8;
21559 }
21560 }
21561 if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
21562 if (pWF->wBitsPerSample == 32) {
21563 return ma_format_f32;
21564 }
21565 if (pWF->wBitsPerSample == 64) {
21566 /*return ma_format_f64;*/
21567 }
21568 }
21569 }
21570
21571 return ma_format_unknown;
21572}
21573#endif
21574
21575
21576/*******************************************************************************
21577
21578WASAPI Backend
21579
21580*******************************************************************************/
21581#ifdef MA_HAS_WASAPI
21582#if 0
21583#if defined(_MSC_VER)
21584 #pragma warning(push)
21585 #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */
21586#endif
21587#include <audioclient.h>
21588#include <mmdeviceapi.h>
21589#if defined(_MSC_VER)
21590 #pragma warning(pop)
21591#endif
21592#endif /* 0 */
21593
21594static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType);
21595
21596/* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */
21597#define MA_WIN32_WINNT_VISTA 0x0600
21598#define MA_VER_MINORVERSION 0x01
21599#define MA_VER_MAJORVERSION 0x02
21600#define MA_VER_SERVICEPACKMAJOR 0x20
21601#define MA_VER_GREATER_EQUAL 0x03
21602
21603typedef struct {
21604 DWORD dwOSVersionInfoSize;
21605 DWORD dwMajorVersion;
21606 DWORD dwMinorVersion;
21607 DWORD dwBuildNumber;
21608 DWORD dwPlatformId;
21609 WCHAR szCSDVersion[128];
21610 WORD wServicePackMajor;
21611 WORD wServicePackMinor;
21612 WORD wSuiteMask;
21613 BYTE wProductType;
21614 BYTE wReserved;
21615} ma_OSVERSIONINFOEXW;
21616
21617typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
21618typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask);
21619
21620
21621#ifndef PROPERTYKEY_DEFINED
21622#define PROPERTYKEY_DEFINED
21623#ifndef __WATCOMC__
21624typedef struct
21625{
21626 GUID fmtid;
21627 DWORD pid;
21628} PROPERTYKEY;
21629#endif
21630#endif
21631
21632/* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */
21633static MA_INLINE void ma_PropVariantInit(MA_PROPVARIANT* pProp)
21634{
21635 MA_ZERO_OBJECT(pProp);
21636}
21637
21638
21639static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14};
21640static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0};
21641
21642static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */
21643#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK)
21644static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */
21645#endif
21646
21647static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */
21648static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */
21649static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */
21650static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */
21651static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */
21652static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */
21653#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK)
21654static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */
21655static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */
21656static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */
21657#endif
21658
21659static const IID MA_CLSID_MMDeviceEnumerator = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */
21660static const IID MA_IID_IMMDeviceEnumerator = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */
21661
21662#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21663#define MA_MM_DEVICE_STATE_ACTIVE 1
21664#define MA_MM_DEVICE_STATE_DISABLED 2
21665#define MA_MM_DEVICE_STATE_NOTPRESENT 4
21666#define MA_MM_DEVICE_STATE_UNPLUGGED 8
21667
21668typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator;
21669typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection;
21670typedef struct ma_IMMDevice ma_IMMDevice;
21671#else
21672typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler;
21673typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation;
21674#endif
21675typedef struct ma_IPropertyStore ma_IPropertyStore;
21676typedef struct ma_IAudioClient ma_IAudioClient;
21677typedef struct ma_IAudioClient2 ma_IAudioClient2;
21678typedef struct ma_IAudioClient3 ma_IAudioClient3;
21679typedef struct ma_IAudioRenderClient ma_IAudioRenderClient;
21680typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient;
21681
21682typedef ma_int64 MA_REFERENCE_TIME;
21683
21684#define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000
21685#define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000
21686#define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000
21687#define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000
21688#define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
21689#define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
21690#define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
21691#define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000
21692#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000
21693#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000
21694
21695/* Buffer flags. */
21696#define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1
21697#define MA_AUDCLNT_BUFFERFLAGS_SILENT 2
21698#define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4
21699
21700typedef enum
21701{
21702 ma_eRender = 0,
21703 ma_eCapture = 1,
21704 ma_eAll = 2
21705} ma_EDataFlow;
21706
21707typedef enum
21708{
21709 ma_eConsole = 0,
21710 ma_eMultimedia = 1,
21711 ma_eCommunications = 2
21712} ma_ERole;
21713
21714typedef enum
21715{
21716 MA_AUDCLNT_SHAREMODE_SHARED,
21717 MA_AUDCLNT_SHAREMODE_EXCLUSIVE
21718} MA_AUDCLNT_SHAREMODE;
21719
21720typedef enum
21721{
21722 MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */
21723} MA_AUDIO_STREAM_CATEGORY;
21724
21725typedef struct
21726{
21727 ma_uint32 cbSize;
21728 BOOL bIsOffload;
21729 MA_AUDIO_STREAM_CATEGORY eCategory;
21730} ma_AudioClientProperties;
21731
21732/* IUnknown */
21733typedef struct
21734{
21735 /* IUnknown */
21736 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject);
21737 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis);
21738 ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis);
21739} ma_IUnknownVtbl;
21740struct ma_IUnknown
21741{
21742 ma_IUnknownVtbl* lpVtbl;
21743};
21744static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
21745static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); }
21746static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); }
21747
21748#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
21749 /* IMMNotificationClient */
21750 typedef struct
21751 {
21752 /* IUnknown */
21753 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject);
21754 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis);
21755 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis);
21756
21757 /* IMMNotificationClient */
21758 HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState);
21759 HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID);
21760 HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID);
21761 HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID);
21762 HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key);
21763 } ma_IMMNotificationClientVtbl;
21764
21765 /* IMMDeviceEnumerator */
21766 typedef struct
21767 {
21768 /* IUnknown */
21769 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject);
21770 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis);
21771 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis);
21772
21773 /* IMMDeviceEnumerator */
21774 HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices);
21775 HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint);
21776 HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice);
21777 HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
21778 HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
21779 } ma_IMMDeviceEnumeratorVtbl;
21780 struct ma_IMMDeviceEnumerator
21781 {
21782 ma_IMMDeviceEnumeratorVtbl* lpVtbl;
21783 };
21784 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
21785 static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); }
21786 static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); }
21787 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); }
21788 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); }
21789 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); }
21790 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); }
21791 static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); }
21792
21793
21794 /* IMMDeviceCollection */
21795 typedef struct
21796 {
21797 /* IUnknown */
21798 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject);
21799 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis);
21800 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis);
21801
21802 /* IMMDeviceCollection */
21803 HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices);
21804 HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice);
21805 } ma_IMMDeviceCollectionVtbl;
21806 struct ma_IMMDeviceCollection
21807 {
21808 ma_IMMDeviceCollectionVtbl* lpVtbl;
21809 };
21810 static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
21811 static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); }
21812 static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); }
21813 static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); }
21814 static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); }
21815
21816
21817 /* IMMDevice */
21818 typedef struct
21819 {
21820 /* IUnknown */
21821 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject);
21822 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis);
21823 ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis);
21824
21825 /* IMMDevice */
21826 HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface);
21827 HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties);
21828 HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, WCHAR** pID);
21829 HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState);
21830 } ma_IMMDeviceVtbl;
21831 struct ma_IMMDevice
21832 {
21833 ma_IMMDeviceVtbl* lpVtbl;
21834 };
21835 static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
21836 static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); }
21837 static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); }
21838 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); }
21839 static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); }
21840 static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, WCHAR** pID) { return pThis->lpVtbl->GetId(pThis, pID); }
21841 static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); }
21842#else
21843 /* IActivateAudioInterfaceAsyncOperation */
21844 typedef struct
21845 {
21846 /* IUnknown */
21847 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject);
21848 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
21849 ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
21850
21851 /* IActivateAudioInterfaceAsyncOperation */
21852 HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface);
21853 } ma_IActivateAudioInterfaceAsyncOperationVtbl;
21854 struct ma_IActivateAudioInterfaceAsyncOperation
21855 {
21856 ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl;
21857 };
21858 static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
21859 static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); }
21860 static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); }
21861 static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); }
21862#endif
21863
21864/* IPropertyStore */
21865typedef struct
21866{
21867 /* IUnknown */
21868 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject);
21869 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis);
21870 ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis);
21871
21872 /* IPropertyStore */
21873 HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount);
21874 HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey);
21875 HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar);
21876 HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar);
21877 HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis);
21878} ma_IPropertyStoreVtbl;
21879struct ma_IPropertyStore
21880{
21881 ma_IPropertyStoreVtbl* lpVtbl;
21882};
21883static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
21884static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); }
21885static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); }
21886static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); }
21887static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); }
21888static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); }
21889static 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); }
21890static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); }
21891
21892
21893/* IAudioClient */
21894typedef struct
21895{
21896 /* IUnknown */
21897 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject);
21898 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis);
21899 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis);
21900
21901 /* IAudioClient */
21902 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);
21903 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames);
21904 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency);
21905 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames);
21906 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch);
21907 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat);
21908 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
21909 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis);
21910 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis);
21911 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis);
21912 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle);
21913 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp);
21914} ma_IAudioClientVtbl;
21915struct ma_IAudioClient
21916{
21917 ma_IAudioClientVtbl* lpVtbl;
21918};
21919static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
21920static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
21921static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); }
21922static 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); }
21923static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
21924static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
21925static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
21926static 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); }
21927static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
21928static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
21929static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); }
21930static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); }
21931static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); }
21932static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
21933static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
21934
21935/* IAudioClient2 */
21936typedef struct
21937{
21938 /* IUnknown */
21939 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject);
21940 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis);
21941 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis);
21942
21943 /* IAudioClient */
21944 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);
21945 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames);
21946 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency);
21947 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames);
21948 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch);
21949 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat);
21950 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
21951 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis);
21952 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis);
21953 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis);
21954 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle);
21955 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp);
21956
21957 /* IAudioClient2 */
21958 HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
21959 HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties);
21960 HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
21961} ma_IAudioClient2Vtbl;
21962struct ma_IAudioClient2
21963{
21964 ma_IAudioClient2Vtbl* lpVtbl;
21965};
21966static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
21967static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); }
21968static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); }
21969static 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); }
21970static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
21971static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
21972static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
21973static 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); }
21974static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
21975static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
21976static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); }
21977static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); }
21978static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); }
21979static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
21980static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
21981static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
21982static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
21983static 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); }
21984
21985
21986/* IAudioClient3 */
21987typedef struct
21988{
21989 /* IUnknown */
21990 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject);
21991 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis);
21992 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis);
21993
21994 /* IAudioClient */
21995 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);
21996 HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames);
21997 HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency);
21998 HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames);
21999 HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch);
22000 HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat);
22001 HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
22002 HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis);
22003 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis);
22004 HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis);
22005 HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle);
22006 HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp);
22007
22008 /* IAudioClient2 */
22009 HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
22010 HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties);
22011 HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
22012
22013 /* IAudioClient3 */
22014 HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames);
22015 HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames);
22016 HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
22017} ma_IAudioClient3Vtbl;
22018struct ma_IAudioClient3
22019{
22020 ma_IAudioClient3Vtbl* lpVtbl;
22021};
22022static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
22023static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); }
22024static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); }
22025static 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); }
22026static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
22027static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
22028static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
22029static 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); }
22030static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
22031static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
22032static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); }
22033static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); }
22034static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); }
22035static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
22036static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
22037static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
22038static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
22039static 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); }
22040static 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); }
22041static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); }
22042static 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); }
22043
22044
22045/* IAudioRenderClient */
22046typedef struct
22047{
22048 /* IUnknown */
22049 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject);
22050 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis);
22051 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis);
22052
22053 /* IAudioRenderClient */
22054 HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData);
22055 HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags);
22056} ma_IAudioRenderClientVtbl;
22057struct ma_IAudioRenderClient
22058{
22059 ma_IAudioRenderClientVtbl* lpVtbl;
22060};
22061static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
22062static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
22063static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); }
22064static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); }
22065static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); }
22066
22067
22068/* IAudioCaptureClient */
22069typedef struct
22070{
22071 /* IUnknown */
22072 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject);
22073 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis);
22074 ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis);
22075
22076 /* IAudioRenderClient */
22077 HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition);
22078 HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead);
22079 HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket);
22080} ma_IAudioCaptureClientVtbl;
22081struct ma_IAudioCaptureClient
22082{
22083 ma_IAudioCaptureClientVtbl* lpVtbl;
22084};
22085static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
22086static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
22087static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); }
22088static 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); }
22089static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); }
22090static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); }
22091
22092#if defined(MA_WIN32_UWP)
22093/* mmdevapi Functions */
22094typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(const wchar_t* deviceInterfacePath, const IID* riid, MA_PROPVARIANT* activationParams, ma_IActivateAudioInterfaceCompletionHandler* completionHandler, ma_IActivateAudioInterfaceAsyncOperation** activationOperation);
22095#endif
22096
22097/* Avrt Functions */
22098typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsA)(const char* TaskName, DWORD* TaskIndex);
22099typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle);
22100
22101#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK)
22102typedef struct ma_completion_handler_uwp ma_completion_handler_uwp;
22103
22104typedef struct
22105{
22106 /* IUnknown */
22107 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject);
22108 ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis);
22109 ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis);
22110
22111 /* IActivateAudioInterfaceCompletionHandler */
22112 HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation);
22113} ma_completion_handler_uwp_vtbl;
22114struct ma_completion_handler_uwp
22115{
22116 ma_completion_handler_uwp_vtbl* lpVtbl;
22117 MA_ATOMIC(4, ma_uint32) counter;
22118 HANDLE hEvent;
22119};
22120
22121static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject)
22122{
22123 /*
22124 We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To
22125 "implement" this, we just make sure we return pThis when the IAgileObject is requested.
22126 */
22127 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)) {
22128 *ppObject = NULL;
22129 return E_NOINTERFACE;
22130 }
22131
22132 /* Getting here means the IID is IUnknown or IMMNotificationClient. */
22133 *ppObject = (void*)pThis;
22134 ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis);
22135 return S_OK;
22136}
22137
22138static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis)
22139{
22140 return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1;
22141}
22142
22143static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis)
22144{
22145 ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1;
22146 if (newRefCount == 0) {
22147 return 0; /* We don't free anything here because we never allocate the object on the heap. */
22148 }
22149
22150 return (ULONG)newRefCount;
22151}
22152
22153static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation)
22154{
22155 (void)pActivateOperation;
22156 SetEvent(pThis->hEvent);
22157 return S_OK;
22158}
22159
22160
22161static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = {
22162 ma_completion_handler_uwp_QueryInterface,
22163 ma_completion_handler_uwp_AddRef,
22164 ma_completion_handler_uwp_Release,
22165 ma_completion_handler_uwp_ActivateCompleted
22166};
22167
22168static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler)
22169{
22170 MA_ASSERT(pHandler != NULL);
22171 MA_ZERO_OBJECT(pHandler);
22172
22173 pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance;
22174 pHandler->counter = 1;
22175 pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
22176 if (pHandler->hEvent == NULL) {
22177 return ma_result_from_GetLastError(GetLastError());
22178 }
22179
22180 return MA_SUCCESS;
22181}
22182
22183static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler)
22184{
22185 if (pHandler->hEvent != NULL) {
22186 CloseHandle(pHandler->hEvent);
22187 }
22188}
22189
22190static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler)
22191{
22192 WaitForSingleObject((HANDLE)pHandler->hEvent, INFINITE);
22193}
22194#endif /* !MA_WIN32_DESKTOP */
22195
22196/* We need a virtual table for our notification client object that's used for detecting changes to the default device. */
22197#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22198static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject)
22199{
22200 /*
22201 We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else
22202 we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK.
22203 */
22204 if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) {
22205 *ppObject = NULL;
22206 return E_NOINTERFACE;
22207 }
22208
22209 /* Getting here means the IID is IUnknown or IMMNotificationClient. */
22210 *ppObject = (void*)pThis;
22211 ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis);
22212 return S_OK;
22213}
22214
22215static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis)
22216{
22217 return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1;
22218}
22219
22220static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis)
22221{
22222 ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1;
22223 if (newRefCount == 0) {
22224 return 0; /* We don't free anything here because we never allocate the object on the heap. */
22225 }
22226
22227 return (ULONG)newRefCount;
22228}
22229
22230static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState)
22231{
22232 ma_bool32 isThisDevice = MA_FALSE;
22233 ma_bool32 isCapture = MA_FALSE;
22234 ma_bool32 isPlayback = MA_FALSE;
22235
22236#ifdef MA_DEBUG_OUTPUT
22237 /*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);*/
22238#endif
22239
22240 /*
22241 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
22242 that the device is disabled or has been unplugged.
22243 */
22244 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)) {
22245 isCapture = MA_TRUE;
22246 if (ma_strcmp_WCHAR(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) {
22247 isThisDevice = MA_TRUE;
22248 }
22249 }
22250
22251 if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) {
22252 isPlayback = MA_TRUE;
22253 if (ma_strcmp_WCHAR(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) {
22254 isThisDevice = MA_TRUE;
22255 }
22256 }
22257
22258
22259 /*
22260 If the device ID matches our device we need to mark our device as detached and stop it. When a
22261 device is added in OnDeviceAdded(), we'll restart it. We only mark it as detached if the device
22262 was started at the time of being removed.
22263 */
22264 if (isThisDevice) {
22265 if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) {
22266 /*
22267 Unplugged or otherwise unavailable. Mark as detached if we were in a playing state. We'll
22268 use this to determine whether or not we need to automatically start the device when it's
22269 plugged back in again.
22270 */
22271 if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) {
22272 if (isPlayback) {
22273 pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE;
22274 }
22275 if (isCapture) {
22276 pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE;
22277 }
22278
22279 ma_device_stop(pThis->pDevice);
22280 }
22281 }
22282
22283 if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) {
22284 /* The device was activated. If we were detached, we need to start it again. */
22285 ma_bool8 tryRestartingDevice = MA_FALSE;
22286
22287 if (isPlayback) {
22288 if (pThis->pDevice->wasapi.isDetachedPlayback) {
22289 pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
22290 ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback);
22291 tryRestartingDevice = MA_TRUE;
22292 }
22293 }
22294
22295 if (isCapture) {
22296 if (pThis->pDevice->wasapi.isDetachedCapture) {
22297 pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
22298 ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
22299 tryRestartingDevice = MA_TRUE;
22300 }
22301 }
22302
22303 if (tryRestartingDevice) {
22304 if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) {
22305 ma_device_start(pThis->pDevice);
22306 }
22307 }
22308 }
22309 }
22310
22311 return S_OK;
22312}
22313
22314static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID)
22315{
22316#ifdef MA_DEBUG_OUTPUT
22317 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
22318#endif
22319
22320 /* We don't need to worry about this event for our purposes. */
22321 (void)pThis;
22322 (void)pDeviceID;
22323 return S_OK;
22324}
22325
22326static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID)
22327{
22328#ifdef MA_DEBUG_OUTPUT
22329 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
22330#endif
22331
22332 /* We don't need to worry about this event for our purposes. */
22333 (void)pThis;
22334 (void)pDeviceID;
22335 return S_OK;
22336}
22337
22338static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID)
22339{
22340#ifdef MA_DEBUG_OUTPUT
22341 /*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)");*/
22342#endif
22343
22344 (void)role;
22345
22346 /* We only care about devices with the same data flow as the current device. */
22347 if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) ||
22348 (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture) ||
22349 (pThis->pDevice->type == ma_device_type_loopback && dataFlow != ma_eRender)) {
22350 ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n");
22351 return S_OK;
22352 }
22353
22354 /* We need to consider dataFlow as ma_eCapture if device is ma_device_type_loopback */
22355 if (pThis->pDevice->type == ma_device_type_loopback) {
22356 dataFlow = ma_eCapture;
22357 }
22358
22359 /* Don't do automatic stream routing if we're not allowed. */
22360 if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) ||
22361 (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) {
22362 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");
22363 return S_OK;
22364 }
22365
22366 /*
22367 Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to
22368 AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once
22369 it's fixed.
22370 */
22371 if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) ||
22372 (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) {
22373 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");
22374 return S_OK;
22375 }
22376
22377
22378
22379 /*
22380 Second attempt at device rerouting. We're going to retrieve the device's state at the time of
22381 the route change. We're then going to stop the device, reinitialize the device, and then start
22382 it again if the state before stopping was ma_device_state_started.
22383 */
22384 {
22385 ma_uint32 previousState = ma_device_get_state(pThis->pDevice);
22386 ma_bool8 restartDevice = MA_FALSE;
22387
22388 if (previousState == ma_device_state_uninitialized || previousState == ma_device_state_starting) {
22389 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");
22390 return S_OK;
22391 }
22392
22393 if (previousState == ma_device_state_started) {
22394 ma_device_stop(pThis->pDevice);
22395 restartDevice = MA_TRUE;
22396 }
22397
22398 if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */
22399 ma_mutex_lock(&pThis->pDevice->wasapi.rerouteLock);
22400 {
22401 if (dataFlow == ma_eRender) {
22402 ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback);
22403
22404 if (pThis->pDevice->wasapi.isDetachedPlayback) {
22405 pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
22406
22407 if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) {
22408 restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */
22409 }
22410 else {
22411 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. */
22412 }
22413 }
22414 }
22415 else {
22416 ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
22417
22418 if (pThis->pDevice->wasapi.isDetachedCapture) {
22419 pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
22420
22421 if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) {
22422 restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */
22423 }
22424 else {
22425 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. */
22426 }
22427 }
22428 }
22429 }
22430 ma_mutex_unlock(&pThis->pDevice->wasapi.rerouteLock);
22431
22432 if (restartDevice) {
22433 ma_device_start(pThis->pDevice);
22434 }
22435 }
22436 }
22437
22438 return S_OK;
22439}
22440
22441static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key)
22442{
22443#ifdef MA_DEBUG_OUTPUT
22444 /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/
22445#endif
22446
22447 (void)pThis;
22448 (void)pDeviceID;
22449 (void)key;
22450 return S_OK;
22451}
22452
22453static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = {
22454 ma_IMMNotificationClient_QueryInterface,
22455 ma_IMMNotificationClient_AddRef,
22456 ma_IMMNotificationClient_Release,
22457 ma_IMMNotificationClient_OnDeviceStateChanged,
22458 ma_IMMNotificationClient_OnDeviceAdded,
22459 ma_IMMNotificationClient_OnDeviceRemoved,
22460 ma_IMMNotificationClient_OnDefaultDeviceChanged,
22461 ma_IMMNotificationClient_OnPropertyValueChanged
22462};
22463#endif /* MA_WIN32_DESKTOP */
22464
22465static const char* ma_to_usage_string__wasapi(ma_wasapi_usage usage)
22466{
22467 switch (usage)
22468 {
22469 case ma_wasapi_usage_default: return NULL;
22470 case ma_wasapi_usage_games: return "Games";
22471 case ma_wasapi_usage_pro_audio: return "Pro Audio";
22472 default: break;
22473 }
22474
22475 return NULL;
22476}
22477
22478#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22479typedef ma_IMMDevice ma_WASAPIDeviceInterface;
22480#else
22481typedef ma_IUnknown ma_WASAPIDeviceInterface;
22482#endif
22483
22484
22485#define MA_CONTEXT_COMMAND_QUIT__WASAPI 1
22486#define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI 2
22487#define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3
22488
22489static ma_context_command__wasapi ma_context_init_command__wasapi(int code)
22490{
22492
22493 MA_ZERO_OBJECT(&cmd);
22494 cmd.code = code;
22495
22496 return cmd;
22497}
22498
22499static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd)
22500{
22501 /* For now we are doing everything synchronously, but I might relax this later if the need arises. */
22502 ma_result result;
22503 ma_bool32 isUsingLocalEvent = MA_FALSE;
22504 ma_event localEvent;
22505
22506 MA_ASSERT(pContext != NULL);
22507 MA_ASSERT(pCmd != NULL);
22508
22509 if (pCmd->pEvent == NULL) {
22510 isUsingLocalEvent = MA_TRUE;
22511
22512 result = ma_event_init(&localEvent);
22513 if (result != MA_SUCCESS) {
22514 return result; /* Failed to create the event for this command. */
22515 }
22516 }
22517
22518 /* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */
22519 ma_mutex_lock(&pContext->wasapi.commandLock);
22520 {
22521 ma_uint32 index;
22522
22523 /* Spin until we've got some space available. */
22524 while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) {
22525 ma_yield();
22526 }
22527
22528 /* Space is now available. Can safely add to the list. */
22529 index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands);
22530 pContext->wasapi.commands[index] = *pCmd;
22531 pContext->wasapi.commands[index].pEvent = &localEvent;
22532 pContext->wasapi.commandCount += 1;
22533
22534 /* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */
22535 ma_semaphore_release(&pContext->wasapi.commandSem);
22536 }
22537 ma_mutex_unlock(&pContext->wasapi.commandLock);
22538
22539 if (isUsingLocalEvent) {
22540 ma_event_wait(&localEvent);
22541 ma_event_uninit(&localEvent);
22542 }
22543
22544 return MA_SUCCESS;
22545}
22546
22547static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd)
22548{
22549 ma_result result = MA_SUCCESS;
22550
22551 MA_ASSERT(pContext != NULL);
22552 MA_ASSERT(pCmd != NULL);
22553
22554 result = ma_semaphore_wait(&pContext->wasapi.commandSem);
22555 if (result == MA_SUCCESS) {
22556 ma_mutex_lock(&pContext->wasapi.commandLock);
22557 {
22558 *pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex];
22559 pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands);
22560 pContext->wasapi.commandCount -= 1;
22561 }
22562 ma_mutex_unlock(&pContext->wasapi.commandLock);
22563 }
22564
22565 return result;
22566}
22567
22568static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData)
22569{
22570 ma_result result;
22571 ma_context* pContext = (ma_context*)pUserData;
22572 MA_ASSERT(pContext != NULL);
22573
22574 for (;;) {
22576 result = ma_context_next_command__wasapi(pContext, &cmd);
22577 if (result != MA_SUCCESS) {
22578 break;
22579 }
22580
22581 switch (cmd.code)
22582 {
22583 case MA_CONTEXT_COMMAND_QUIT__WASAPI:
22584 {
22585 /* Do nothing. Handled after the switch. */
22586 } break;
22587
22588 case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI:
22589 {
22591 *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService));
22592 } else {
22593 *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService));
22594 }
22595 } break;
22596
22597 case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI:
22598 {
22600 if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) {
22601 ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback);
22602 cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL;
22603 }
22604 }
22605
22607 if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) {
22608 ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture);
22609 cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL;
22610 }
22611 }
22612 } break;
22613
22614 default:
22615 {
22616 /* Unknown command. Ignore it, but trigger an assert in debug mode so we're aware of it. */
22617 MA_ASSERT(MA_FALSE);
22618 } break;
22619 }
22620
22621 if (cmd.pEvent != NULL) {
22623 }
22624
22625 if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) {
22626 break; /* Received a quit message. Get out of here. */
22627 }
22628 }
22629
22630 return (ma_thread_result)0;
22631}
22632
22633static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService)
22634{
22635 ma_result result;
22636 ma_result cmdResult;
22637 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI);
22638 cmd.data.createAudioClient.deviceType = deviceType;
22639 cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient;
22640 cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService;
22641 cmd.data.createAudioClient.pResult = &cmdResult; /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */
22642
22643 result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */
22644 if (result != MA_SUCCESS) {
22645 return result;
22646 }
22647
22648 return *cmd.data.createAudioClient.pResult;
22649}
22650
22651#if 0 /* Not used at the moment, but leaving here for future use. */
22652static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType)
22653{
22654 ma_result result;
22655 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI);
22656 cmd.data.releaseAudioClient.pDevice = pDevice;
22657 cmd.data.releaseAudioClient.deviceType = deviceType;
22658
22659 result = ma_context_post_command__wasapi(pDevice->pContext, &cmd); /* This will not return until the command has actually been run. */
22660 if (result != MA_SUCCESS) {
22661 return result;
22662 }
22663
22664 return MA_SUCCESS;
22665}
22666#endif
22667
22668
22669static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo)
22670{
22671 MA_ASSERT(pWF != NULL);
22672 MA_ASSERT(pInfo != NULL);
22673
22674 if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) {
22675 return; /* Too many data formats. Need to ignore this one. Don't think this should ever happen with WASAPI. */
22676 }
22677
22678 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format = ma_format_from_WAVEFORMATEX(pWF);
22679 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels = pWF->nChannels;
22680 pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec;
22682 pInfo->nativeDataFormatCount += 1;
22683}
22684
22685static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo)
22686{
22687 HRESULT hr;
22688 MA_WAVEFORMATEX* pWF = NULL;
22689
22690 MA_ASSERT(pAudioClient != NULL);
22691 MA_ASSERT(pInfo != NULL);
22692
22693 /* Shared Mode. We use GetMixFormat() here. */
22694 hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (MA_WAVEFORMATEX**)&pWF);
22695 if (SUCCEEDED(hr)) {
22696 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo);
22697 } else {
22698 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval.");
22699 return ma_result_from_HRESULT(hr);
22700 }
22701
22702 /*
22703 Exclusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on
22704 UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on
22705 out, MA_SUCCESS is guaranteed to be returned.
22706 */
22707 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22708 {
22709 ma_IPropertyStore *pProperties;
22710
22711 /*
22712 The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is
22713 correct which will simplify our searching.
22714 */
22715 hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties);
22716 if (SUCCEEDED(hr)) {
22717 MA_PROPVARIANT var;
22718 ma_PropVariantInit(&var);
22719
22720 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var);
22721 if (SUCCEEDED(hr)) {
22722 pWF = (MA_WAVEFORMATEX*)var.blob.pBlobData;
22723
22724 /*
22725 In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format
22726 first. If this fails, fall back to a search.
22727 */
22728 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL);
22729 if (SUCCEEDED(hr)) {
22730 /* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */
22731 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo);
22732 } else {
22733 /*
22734 The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel
22735 count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format.
22736 */
22737 ma_uint32 channels = pWF->nChannels;
22738 ma_channel defaultChannelMap[MA_MAX_CHANNELS];
22739 MA_WAVEFORMATEXTENSIBLE wf;
22740 ma_bool32 found;
22741 ma_uint32 iFormat;
22742
22743 /* Make sure we don't overflow the channel map. */
22744 if (channels > MA_MAX_CHANNELS) {
22745 channels = MA_MAX_CHANNELS;
22746 }
22747
22748 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels);
22749
22750 MA_ZERO_OBJECT(&wf);
22751 wf.cbSize = sizeof(wf);
22752 wf.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
22753 wf.nChannels = (WORD)channels;
22754 wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels);
22755
22756 found = MA_FALSE;
22757 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) {
22758 ma_format format = g_maFormatPriorities[iFormat];
22759 ma_uint32 iSampleRate;
22760
22761 wf.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8);
22762 wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8);
22763 wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;
22764 wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.wBitsPerSample;
22765 if (format == ma_format_f32) {
22766 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
22767 } else {
22768 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
22769 }
22770
22771 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) {
22772 wf.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate];
22773
22774 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (MA_WAVEFORMATEX*)&wf, NULL);
22775 if (SUCCEEDED(hr)) {
22776 ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo);
22777 found = MA_TRUE;
22778 break;
22779 }
22780 }
22781
22782 if (found) {
22783 break;
22784 }
22785 }
22786
22787 ma_PropVariantClear(pContext, &var);
22788
22789 if (!found) {
22790 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to find suitable device format for device info retrieval.");
22791 }
22792 }
22793 } else {
22794 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to retrieve device format for device info retrieval.");
22795 }
22796
22797 ma_IPropertyStore_Release(pProperties);
22798 } else {
22799 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval.");
22800 }
22801 }
22802 #else
22803 {
22804 (void)pMMDevice; /* Unused. */
22805 }
22806 #endif
22807
22808 return MA_SUCCESS;
22809}
22810
22811#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
22812static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType)
22813{
22814 if (deviceType == ma_device_type_playback) {
22815 return ma_eRender;
22816 } else if (deviceType == ma_device_type_capture) {
22817 return ma_eCapture;
22818 } else {
22819 MA_ASSERT(MA_FALSE);
22820 return ma_eRender; /* Should never hit this. */
22821 }
22822}
22823
22824static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator)
22825{
22826 HRESULT hr;
22827 ma_IMMDeviceEnumerator* pDeviceEnumerator;
22828
22829 MA_ASSERT(pContext != NULL);
22830 MA_ASSERT(ppDeviceEnumerator != NULL);
22831
22832 *ppDeviceEnumerator = NULL; /* Safety. */
22833
22834 hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
22835 if (FAILED(hr)) {
22836 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
22837 return ma_result_from_HRESULT(hr);
22838 }
22839
22840 *ppDeviceEnumerator = pDeviceEnumerator;
22841
22842 return MA_SUCCESS;
22843}
22844
22845static WCHAR* ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType)
22846{
22847 HRESULT hr;
22848 ma_IMMDevice* pMMDefaultDevice = NULL;
22849 WCHAR* pDefaultDeviceID = NULL;
22850 ma_EDataFlow dataFlow;
22851 ma_ERole role;
22852
22853 MA_ASSERT(pContext != NULL);
22854 MA_ASSERT(pDeviceEnumerator != NULL);
22855
22856 (void)pContext;
22857
22858 /* Grab the EDataFlow type from the device type. */
22859 dataFlow = ma_device_type_to_EDataFlow(deviceType);
22860
22861 /* The role is always eConsole, but we may make this configurable later. */
22862 role = ma_eConsole;
22863
22864 hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice);
22865 if (FAILED(hr)) {
22866 return NULL;
22867 }
22868
22869 hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID);
22870
22871 ma_IMMDevice_Release(pMMDefaultDevice);
22872 pMMDefaultDevice = NULL;
22873
22874 if (FAILED(hr)) {
22875 return NULL;
22876 }
22877
22878 return pDefaultDeviceID;
22879}
22880
22881static WCHAR* ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */
22882{
22883 ma_result result;
22884 ma_IMMDeviceEnumerator* pDeviceEnumerator;
22885 WCHAR* pDefaultDeviceID = NULL;
22886
22887 MA_ASSERT(pContext != NULL);
22888
22889 result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator);
22890 if (result != MA_SUCCESS) {
22891 return NULL;
22892 }
22893
22894 pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
22895
22896 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
22897 return pDefaultDeviceID;
22898}
22899
22900static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice)
22901{
22902 ma_IMMDeviceEnumerator* pDeviceEnumerator;
22903 HRESULT hr;
22904 HRESULT CoInitializeResult;
22905
22906 MA_ASSERT(pContext != NULL);
22907 MA_ASSERT(ppMMDevice != NULL);
22908
22909 /*
22910 This weird COM init/uninit here is a hack to work around a crash when changing devices. What is happening is
22911 WASAPI fires a callback from another thread when the device is changed. It's from that thread where this
22912 function is getting called. What I'm suspecting is that the other thread is not initializing COM which in turn
22913 results in CoCreateInstance() failing.
22914
22915 The community has reported that this seems to fix the crash. There are future plans to move all WASAPI operation
22916 over to a single thread to make everything safer, but in the meantime while we wait for that to come online I'm
22917 happy enough to use this hack instead.
22918
22919 CoUninitialize should only be called if we successfully initialized. S_OK and S_FALSE both mean that we need to
22920 call CoUninitialize since the internal ref count was increased. RPC_E_CHANGED_MODE means that CoInitializeEx was
22921 called with a different COINIT value, and we don't call CoUninitialize in that case. Other errors are possible,
22922 so we check for S_OK and S_FALSE specifically.
22923 */
22924 CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);
22925 {
22926 hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
22927 }
22928 if (CoInitializeResult == S_OK || CoInitializeResult == S_FALSE) { ma_CoUninitialize(pContext); }
22929
22930 if (FAILED(hr)) { /* <-- This is checking the call above to ma_CoCreateInstance(). */
22931 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n");
22932 return ma_result_from_HRESULT(hr);
22933 }
22934
22935 if (pDeviceID == NULL) {
22936 hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice);
22937 } else {
22938 hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice);
22939 }
22940
22941 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
22942 if (FAILED(hr)) {
22943 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.\n");
22944 return ma_result_from_HRESULT(hr);
22945 }
22946
22947 return MA_SUCCESS;
22948}
22949
22950static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID)
22951{
22952 WCHAR* pDeviceIDString;
22953 HRESULT hr;
22954
22955 MA_ASSERT(pDeviceID != NULL);
22956
22957 hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString);
22958 if (SUCCEEDED(hr)) {
22959 size_t idlen = ma_strlen_WCHAR(pDeviceIDString);
22960 if (idlen+1 > ma_countof(pDeviceID->wasapi)) {
22961 ma_CoTaskMemFree(pContext, pDeviceIDString);
22962 MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must have changed and is too long to fit in our fixed sized buffer. */
22963 return MA_ERROR;
22964 }
22965
22966 MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t));
22967 pDeviceID->wasapi[idlen] = '\0';
22968
22969 ma_CoTaskMemFree(pContext, pDeviceIDString);
22970
22971 return MA_SUCCESS;
22972 }
22973
22974 return MA_ERROR;
22975}
22976
22977static 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)
22978{
22979 ma_result result;
22980 HRESULT hr;
22981
22982 MA_ASSERT(pContext != NULL);
22983 MA_ASSERT(pMMDevice != NULL);
22984 MA_ASSERT(pInfo != NULL);
22985
22986 /* ID. */
22987 result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id);
22988 if (result == MA_SUCCESS) {
22989 if (pDefaultDeviceID != NULL) {
22990 if (ma_strcmp_WCHAR(pInfo->id.wasapi, pDefaultDeviceID) == 0) {
22991 pInfo->isDefault = MA_TRUE;
22992 }
22993 }
22994 }
22995
22996 /* Description / Friendly Name */
22997 {
22998 ma_IPropertyStore *pProperties;
22999 hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties);
23000 if (SUCCEEDED(hr)) {
23001 MA_PROPVARIANT var;
23002
23003 ma_PropVariantInit(&var);
23004 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var);
23005 if (SUCCEEDED(hr)) {
23006 WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);
23007 ma_PropVariantClear(pContext, &var);
23008 }
23009
23010 ma_IPropertyStore_Release(pProperties);
23011 }
23012 }
23013
23014 /* Format */
23015 if (!onlySimpleInfo) {
23016 ma_IAudioClient* pAudioClient;
23017 hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
23018 if (SUCCEEDED(hr)) {
23019 result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo);
23020
23021 ma_IAudioClient_Release(pAudioClient);
23022 return result;
23023 } else {
23024 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval.");
23025 return ma_result_from_HRESULT(hr);
23026 }
23027 }
23028
23029 return MA_SUCCESS;
23030}
23031
23032static 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)
23033{
23034 ma_result result = MA_SUCCESS;
23035 UINT deviceCount;
23036 HRESULT hr;
23037 ma_uint32 iDevice;
23038 WCHAR* pDefaultDeviceID = NULL;
23039 ma_IMMDeviceCollection* pDeviceCollection = NULL;
23040
23041 MA_ASSERT(pContext != NULL);
23042 MA_ASSERT(callback != NULL);
23043
23044 /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */
23045 pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
23046
23047 /* We need to enumerate the devices which returns a device collection. */
23048 hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection);
23049 if (SUCCEEDED(hr)) {
23050 hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount);
23051 if (FAILED(hr)) {
23052 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.\n");
23053 result = ma_result_from_HRESULT(hr);
23054 goto done;
23055 }
23056
23057 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
23058 ma_device_info deviceInfo;
23059 ma_IMMDevice* pMMDevice;
23060
23061 MA_ZERO_OBJECT(&deviceInfo);
23062
23063 hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice);
23064 if (SUCCEEDED(hr)) {
23065 result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */
23066
23067 ma_IMMDevice_Release(pMMDevice);
23068 if (result == MA_SUCCESS) {
23069 ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
23070 if (cbResult == MA_FALSE) {
23071 break;
23072 }
23073 }
23074 }
23075 }
23076 }
23077
23078done:
23079 if (pDefaultDeviceID != NULL) {
23080 ma_CoTaskMemFree(pContext, pDefaultDeviceID);
23081 pDefaultDeviceID = NULL;
23082 }
23083
23084 if (pDeviceCollection != NULL) {
23085 ma_IMMDeviceCollection_Release(pDeviceCollection);
23086 pDeviceCollection = NULL;
23087 }
23088
23089 return result;
23090}
23091
23092static 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)
23093{
23094 ma_result result;
23095 HRESULT hr;
23096
23097 MA_ASSERT(pContext != NULL);
23098 MA_ASSERT(ppAudioClient != NULL);
23099 MA_ASSERT(ppMMDevice != NULL);
23100
23101 result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice);
23102 if (result != MA_SUCCESS) {
23103 return result;
23104 }
23105
23106 hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, pActivationParams, (void**)ppAudioClient);
23107 if (FAILED(hr)) {
23108 return ma_result_from_HRESULT(hr);
23109 }
23110
23111 return MA_SUCCESS;
23112}
23113#else
23114static 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)
23115{
23116 ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
23117 ma_completion_handler_uwp completionHandler;
23118 IID iid;
23119 WCHAR* iidStr;
23120 HRESULT hr;
23121 ma_result result;
23122 HRESULT activateResult;
23123 ma_IUnknown* pActivatedInterface;
23124
23125 MA_ASSERT(pContext != NULL);
23126 MA_ASSERT(ppAudioClient != NULL);
23127
23128 if (pDeviceID != NULL) {
23129 iidStr = (WCHAR*)pDeviceID->wasapi;
23130 } else {
23131 if (deviceType == ma_device_type_capture) {
23132 iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE;
23133 } else {
23134 iid = MA_IID_DEVINTERFACE_AUDIO_RENDER;
23135 }
23136
23137 #if defined(__cplusplus)
23138 hr = StringFromIID(iid, &iidStr);
23139 #else
23140 hr = StringFromIID(&iid, &iidStr);
23141 #endif
23142 if (FAILED(hr)) {
23143 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");
23144 return ma_result_from_HRESULT(hr);
23145 }
23146 }
23147
23148 result = ma_completion_handler_uwp_init(&completionHandler);
23149 if (result != MA_SUCCESS) {
23150 ma_CoTaskMemFree(pContext, iidStr);
23151 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().\n");
23152 return result;
23153 }
23154
23155 hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContext->wasapi.ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
23156 if (FAILED(hr)) {
23157 ma_completion_handler_uwp_uninit(&completionHandler);
23158 ma_CoTaskMemFree(pContext, iidStr);
23159 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.\n");
23160 return ma_result_from_HRESULT(hr);
23161 }
23162
23163 if (pDeviceID == NULL) {
23164 ma_CoTaskMemFree(pContext, iidStr);
23165 }
23166
23167 /* Wait for the async operation for finish. */
23168 ma_completion_handler_uwp_wait(&completionHandler);
23169 ma_completion_handler_uwp_uninit(&completionHandler);
23170
23171 hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);
23172 ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
23173
23174 if (FAILED(hr) || FAILED(activateResult)) {
23175 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.\n");
23176 return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult);
23177 }
23178
23179 /* Here is where we grab the IAudioClient interface. */
23180 hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient);
23181 if (FAILED(hr)) {
23182 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.\n");
23183 return ma_result_from_HRESULT(hr);
23184 }
23185
23186 if (ppActivatedInterface) {
23187 *ppActivatedInterface = pActivatedInterface;
23188 } else {
23189 ma_IUnknown_Release(pActivatedInterface);
23190 }
23191
23192 return MA_SUCCESS;
23193}
23194#endif
23195
23196
23197/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-audioclient_activation_type */
23198typedef enum
23199{
23200 MA_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT,
23201 MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK
23202} MA_AUDIOCLIENT_ACTIVATION_TYPE;
23203
23204/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-process_loopback_mode */
23205typedef enum
23206{
23207 MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE,
23208 MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE
23209} MA_PROCESS_LOOPBACK_MODE;
23210
23211/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params */
23212typedef struct
23213{
23214 DWORD TargetProcessId;
23215 MA_PROCESS_LOOPBACK_MODE ProcessLoopbackMode;
23216} MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS;
23217
23218#if defined(_MSC_VER) && !defined(__clang__)
23219 #pragma warning(push)
23220 #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
23221#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
23222 #pragma GCC diagnostic push
23223 #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
23224 #if defined(__clang__)
23225 #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */
23226 #endif
23227#endif
23228/* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_activation_params */
23229typedef struct
23230{
23231 MA_AUDIOCLIENT_ACTIVATION_TYPE ActivationType;
23232 union
23233 {
23234 MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams;
23235 };
23236} MA_AUDIOCLIENT_ACTIVATION_PARAMS;
23237#if defined(_MSC_VER) && !defined(__clang__)
23238 #pragma warning(pop)
23239#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
23240 #pragma GCC diagnostic pop
23241#endif
23242
23243#define MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L"VAD\\Process_Loopback"
23244
23245static 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)
23246{
23247 ma_result result;
23248 ma_bool32 usingProcessLoopback = MA_FALSE;
23249 MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams;
23250 MA_PROPVARIANT activationParams;
23251 MA_PROPVARIANT* pActivationParams = NULL;
23252 ma_device_id virtualDeviceID;
23253
23254 /* Activation parameters specific to loopback mode. Note that process-specific loopback will only work when a default device ID is specified. */
23255 if (deviceType == ma_device_type_loopback && loopbackProcessID != 0 && pDeviceID == NULL) {
23256 usingProcessLoopback = MA_TRUE;
23257 }
23258
23259 if (usingProcessLoopback) {
23260 MA_ZERO_OBJECT(&audioclientActivationParams);
23261 audioclientActivationParams.ActivationType = MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK;
23262 audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = (loopbackProcessExclude) ? MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE : MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE;
23263 audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID;
23264
23265 ma_PropVariantInit(&activationParams);
23266 activationParams.vt = MA_VT_BLOB;
23267 activationParams.blob.cbSize = sizeof(audioclientActivationParams);
23268 activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams;
23269 pActivationParams = &activationParams;
23270
23271 /* When requesting a specific device ID we need to use a special device ID. */
23272 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. */
23273 pDeviceID = &virtualDeviceID;
23274 } else {
23275 pActivationParams = NULL; /* No activation parameters required. */
23276 }
23277
23278#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
23279 result = ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface);
23280#else
23281 result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface);
23282#endif
23283
23284 /*
23285 If loopback mode was requested with a process ID and initialization failed, it could be because it's
23286 trying to run on an older version of Windows where it's not supported. We need to let the caller
23287 know about this with a log message.
23288 */
23289 if (result != MA_SUCCESS) {
23290 if (usingProcessLoopback) {
23291 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);
23292 }
23293 }
23294
23295 return result;
23296}
23297
23298
23299static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
23300{
23301 /* Different enumeration for desktop and UWP. */
23302#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
23303 /* Desktop */
23304 HRESULT hr;
23305 ma_IMMDeviceEnumerator* pDeviceEnumerator;
23306
23307 hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
23308 if (FAILED(hr)) {
23309 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
23310 return ma_result_from_HRESULT(hr);
23311 }
23312
23313 ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData);
23314 ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData);
23315
23316 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
23317#else
23318 /*
23319 UWP
23320
23321 The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate
23322 over devices without using MMDevice, I'm restricting devices to defaults.
23323
23324 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/
23325 */
23326 if (callback) {
23327 ma_bool32 cbResult = MA_TRUE;
23328
23329 /* Playback. */
23330 if (cbResult) {
23331 ma_device_info deviceInfo;
23332 MA_ZERO_OBJECT(&deviceInfo);
23333 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
23334 deviceInfo.isDefault = MA_TRUE;
23335 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
23336 }
23337
23338 /* Capture. */
23339 if (cbResult) {
23340 ma_device_info deviceInfo;
23341 MA_ZERO_OBJECT(&deviceInfo);
23342 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
23343 deviceInfo.isDefault = MA_TRUE;
23344 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
23345 }
23346 }
23347#endif
23348
23349 return MA_SUCCESS;
23350}
23351
23352static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
23353{
23354#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
23355 ma_result result;
23356 ma_IMMDevice* pMMDevice = NULL;
23357 WCHAR* pDefaultDeviceID = NULL;
23358
23359 result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice);
23360 if (result != MA_SUCCESS) {
23361 return result;
23362 }
23363
23364 /* We need the default device ID so we can set the isDefault flag in the device info. */
23365 pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType);
23366
23367 result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */
23368
23369 if (pDefaultDeviceID != NULL) {
23370 ma_CoTaskMemFree(pContext, pDefaultDeviceID);
23371 pDefaultDeviceID = NULL;
23372 }
23373
23374 ma_IMMDevice_Release(pMMDevice);
23375
23376 return result;
23377#else
23378 ma_IAudioClient* pAudioClient;
23379 ma_result result;
23380
23381 /* UWP currently only uses default devices. */
23382 if (deviceType == ma_device_type_playback) {
23383 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
23384 } else {
23385 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
23386 }
23387
23388 result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, NULL, &pAudioClient, NULL);
23389 if (result != MA_SUCCESS) {
23390 return result;
23391 }
23392
23393 result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo);
23394
23395 pDeviceInfo->isDefault = MA_TRUE; /* UWP only supports default devices. */
23396
23397 ma_IAudioClient_Release(pAudioClient);
23398 return result;
23399#endif
23400}
23401
23402static ma_result ma_device_uninit__wasapi(ma_device* pDevice)
23403{
23404 MA_ASSERT(pDevice != NULL);
23405
23406 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
23407 {
23408 if (pDevice->wasapi.pDeviceEnumerator) {
23409 ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient);
23410 ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator);
23411 }
23412
23413 ma_mutex_uninit(&pDevice->wasapi.rerouteLock);
23414 }
23415 #endif
23416
23417 if (pDevice->wasapi.pRenderClient) {
23418 if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
23419 ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
23420 pDevice->wasapi.pMappedBufferPlayback = NULL;
23421 pDevice->wasapi.mappedBufferPlaybackCap = 0;
23422 pDevice->wasapi.mappedBufferPlaybackLen = 0;
23423 }
23424
23425 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
23426 }
23427 if (pDevice->wasapi.pCaptureClient) {
23428 if (pDevice->wasapi.pMappedBufferCapture != NULL) {
23429 ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
23430 pDevice->wasapi.pMappedBufferCapture = NULL;
23431 pDevice->wasapi.mappedBufferCaptureCap = 0;
23432 pDevice->wasapi.mappedBufferCaptureLen = 0;
23433 }
23434
23435 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
23436 }
23437
23438 if (pDevice->wasapi.pAudioClientPlayback) {
23439 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
23440 }
23441 if (pDevice->wasapi.pAudioClientCapture) {
23442 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
23443 }
23444
23445 if (pDevice->wasapi.hEventPlayback) {
23446 CloseHandle((HANDLE)pDevice->wasapi.hEventPlayback);
23447 }
23448 if (pDevice->wasapi.hEventCapture) {
23449 CloseHandle((HANDLE)pDevice->wasapi.hEventCapture);
23450 }
23451
23452 return MA_SUCCESS;
23453}
23454
23455
23456typedef struct
23457{
23458 /* Input. */
23459 ma_format formatIn;
23460 ma_uint32 channelsIn;
23461 ma_uint32 sampleRateIn;
23462 ma_channel channelMapIn[MA_MAX_CHANNELS];
23463 ma_uint32 periodSizeInFramesIn;
23464 ma_uint32 periodSizeInMillisecondsIn;
23465 ma_uint32 periodsIn;
23466 ma_share_mode shareMode;
23467 ma_performance_profile performanceProfile;
23468 ma_bool32 noAutoConvertSRC;
23469 ma_bool32 noDefaultQualitySRC;
23470 ma_bool32 noHardwareOffloading;
23471 ma_uint32 loopbackProcessID;
23472 ma_bool32 loopbackProcessExclude;
23473
23474 /* Output. */
23475 ma_IAudioClient* pAudioClient;
23476 ma_IAudioRenderClient* pRenderClient;
23477 ma_IAudioCaptureClient* pCaptureClient;
23478 ma_format formatOut;
23479 ma_uint32 channelsOut;
23480 ma_uint32 sampleRateOut;
23481 ma_channel channelMapOut[MA_MAX_CHANNELS];
23482 ma_uint32 periodSizeInFramesOut;
23483 ma_uint32 periodsOut;
23484 ma_bool32 usingAudioClient3;
23485 char deviceName[256];
23486 ma_device_id id;
23487} ma_device_init_internal_data__wasapi;
23488
23489static 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)
23490{
23491 HRESULT hr;
23492 ma_result result = MA_SUCCESS;
23493 const char* errorMsg = "";
23494 MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
23495 DWORD streamFlags = 0;
23496 MA_REFERENCE_TIME periodDurationInMicroseconds;
23497 ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE;
23498 MA_WAVEFORMATEXTENSIBLE wf;
23499 ma_WASAPIDeviceInterface* pDeviceInterface = NULL;
23500 ma_IAudioClient2* pAudioClient2;
23501 ma_uint32 nativeSampleRate;
23502 ma_bool32 usingProcessLoopback = MA_FALSE;
23503
23504 MA_ASSERT(pContext != NULL);
23505 MA_ASSERT(pData != NULL);
23506
23507 /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */
23508 if (deviceType == ma_device_type_duplex) {
23509 return MA_INVALID_ARGS;
23510 }
23511
23512 usingProcessLoopback = deviceType == ma_device_type_loopback && pData->loopbackProcessID != 0 && pDeviceID == NULL;
23513
23514 pData->pAudioClient = NULL;
23515 pData->pRenderClient = NULL;
23516 pData->pCaptureClient = NULL;
23517
23518 streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
23519 if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */
23520 streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
23521 }
23522 if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) {
23523 streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
23524 }
23525 if (deviceType == ma_device_type_loopback) {
23526 streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK;
23527 }
23528
23529 result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, pData->loopbackProcessID, pData->loopbackProcessExclude, &pData->pAudioClient, &pDeviceInterface);
23530 if (result != MA_SUCCESS) {
23531 goto done;
23532 }
23533
23534 MA_ZERO_OBJECT(&wf);
23535
23536 /* Try enabling hardware offloading. */
23537 if (!pData->noHardwareOffloading) {
23538 hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2);
23539 if (SUCCEEDED(hr)) {
23540 BOOL isHardwareOffloadingSupported = 0;
23541 hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported);
23542 if (SUCCEEDED(hr) && isHardwareOffloadingSupported) {
23543 ma_AudioClientProperties clientProperties;
23544 MA_ZERO_OBJECT(&clientProperties);
23545 clientProperties.cbSize = sizeof(clientProperties);
23546 clientProperties.bIsOffload = 1;
23547 clientProperties.eCategory = MA_AudioCategory_Other;
23548 ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties);
23549 }
23550
23551 pAudioClient2->lpVtbl->Release(pAudioClient2);
23552 }
23553 }
23554
23555 /* 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. */
23556 result = MA_FORMAT_NOT_SUPPORTED;
23557 if (pData->shareMode == ma_share_mode_exclusive) {
23558 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
23559 /* In exclusive mode on desktop we always use the backend's native format. */
23560 ma_IPropertyStore* pStore = NULL;
23561 hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore);
23562 if (SUCCEEDED(hr)) {
23563 MA_PROPVARIANT prop;
23564 ma_PropVariantInit(&prop);
23565 hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop);
23566 if (SUCCEEDED(hr)) {
23567 MA_WAVEFORMATEX* pActualFormat = (MA_WAVEFORMATEX*)prop.blob.pBlobData;
23568 hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL);
23569 if (SUCCEEDED(hr)) {
23570 MA_COPY_MEMORY(&wf, pActualFormat, sizeof(MA_WAVEFORMATEXTENSIBLE));
23571 }
23572
23573 ma_PropVariantClear(pContext, &prop);
23574 }
23575
23576 ma_IPropertyStore_Release(pStore);
23577 }
23578 #else
23579 /*
23580 I do not know how to query the device's native format on UWP so for now I'm just disabling support for
23581 exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported()
23582 until you find one that works.
23583
23584 TODO: Add support for exclusive mode to UWP.
23585 */
23586 hr = S_FALSE;
23587 #endif
23588
23589 if (hr == S_OK) {
23590 shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE;
23591 result = MA_SUCCESS;
23592 } else {
23594 }
23595 } else {
23596 /* In shared mode we are always using the format reported by the operating system. */
23597 MA_WAVEFORMATEXTENSIBLE* pNativeFormat = NULL;
23598 hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (MA_WAVEFORMATEX**)&pNativeFormat);
23599 if (hr != S_OK) {
23600 /* When using process-specific loopback, GetMixFormat() seems to always fail. */
23601 if (usingProcessLoopback) {
23602 wf.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
23603 wf.nChannels = 2;
23604 wf.nSamplesPerSec = 44100;
23605 wf.wBitsPerSample = 32;
23606 wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8;
23607 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
23608 wf.cbSize = sizeof(MA_WAVEFORMATEX);
23609
23610 result = MA_SUCCESS;
23611 } else {
23612 result = MA_FORMAT_NOT_SUPPORTED;
23613 }
23614 } else {
23615 /*
23616 I've seen cases where cbSize will be set to sizeof(WAVEFORMATEX) even though the structure itself
23617 is given the format tag of WAVE_FORMAT_EXTENSIBLE. If the format tag is WAVE_FORMAT_EXTENSIBLE
23618 want to make sure we copy the whole WAVEFORMATEXTENSIBLE structure. Otherwise we'll have to be
23619 safe and only copy the WAVEFORMATEX part.
23620 */
23621 if (pNativeFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
23622 MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(MA_WAVEFORMATEXTENSIBLE));
23623 } else {
23624 /* I've seen a case where cbSize was set to 0. Assume sizeof(WAVEFORMATEX) in this case. */
23625 size_t cbSize = pNativeFormat->cbSize;
23626 if (cbSize == 0) {
23627 cbSize = sizeof(MA_WAVEFORMATEX);
23628 }
23629
23630 /* Make sure we don't copy more than the capacity of `wf`. */
23631 if (cbSize > sizeof(wf)) {
23632 cbSize = sizeof(wf);
23633 }
23634
23635 MA_COPY_MEMORY(&wf, pNativeFormat, cbSize);
23636 }
23637
23638 result = MA_SUCCESS;
23639 }
23640
23641 ma_CoTaskMemFree(pContext, pNativeFormat);
23642
23643 shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
23644 }
23645
23646 /* Return an error if we still haven't found a format. */
23647 if (result != MA_SUCCESS) {
23648 errorMsg = "[WASAPI] Failed to find best device mix format.";
23649 goto done;
23650 }
23651
23652 /*
23653 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
23654 WASAPI to perform the sample rate conversion.
23655 */
23656 nativeSampleRate = wf.nSamplesPerSec;
23657 if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) {
23658 wf.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE;
23659 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
23660 }
23661
23662 pData->formatOut = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf);
23663 if (pData->formatOut == ma_format_unknown) {
23664 /*
23665 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
23666 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
23667 completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED.
23668 */
23669 if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
23671 } else {
23672 result = MA_FORMAT_NOT_SUPPORTED;
23673 }
23674
23675 errorMsg = "[WASAPI] Native format not supported.";
23676 goto done;
23677 }
23678
23679 pData->channelsOut = wf.nChannels;
23680 pData->sampleRateOut = wf.nSamplesPerSec;
23681
23682 /*
23683 Get the internal channel map based on the channel mask. There is a possibility that GetMixFormat() returns
23684 a WAVEFORMATEX instead of a WAVEFORMATEXTENSIBLE, in which case the channel mask will be undefined. In this
23685 case we'll just use the default channel map.
23686 */
23687 if (wf.wFormatTag == WAVE_FORMAT_EXTENSIBLE || wf.cbSize >= sizeof(MA_WAVEFORMATEXTENSIBLE)) {
23688 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut);
23689 } else {
23690 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);
23691 }
23692
23693 /* Period size. */
23694 pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS;
23695 pData->periodSizeInFramesOut = pData->periodSizeInFramesIn;
23696 if (pData->periodSizeInFramesOut == 0) {
23697 if (pData->periodSizeInMillisecondsIn == 0) {
23698 if (pData->performanceProfile == ma_performance_profile_low_latency) {
23699 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.nSamplesPerSec);
23700 } else {
23701 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.nSamplesPerSec);
23702 }
23703 } else {
23704 pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.nSamplesPerSec);
23705 }
23706 }
23707
23708 periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.nSamplesPerSec;
23709
23710
23711 /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */
23712 if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
23713 MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10;
23714
23715 /*
23716 If the periodicity is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing
23717 it and trying it again.
23718 */
23719 hr = E_FAIL;
23720 for (;;) {
23721 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL);
23722 if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) {
23723 if (bufferDuration > 500*10000) {
23724 break;
23725 } else {
23726 if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinite loop. Should never happen, but it makes me feel better. */
23727 break;
23728 }
23729
23730 bufferDuration = bufferDuration * 2;
23731 continue;
23732 }
23733 } else {
23734 break;
23735 }
23736 }
23737
23738 if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
23739 ma_uint32 bufferSizeInFrames;
23740 hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
23741 if (SUCCEEDED(hr)) {
23742 bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.nSamplesPerSec * bufferSizeInFrames) + 0.5);
23743
23744 /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */
23745 ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
23746
23747 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
23748 hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient);
23749 #else
23750 hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient);
23751 #endif
23752
23753 if (SUCCEEDED(hr)) {
23754 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL);
23755 }
23756 }
23757 }
23758
23759 if (FAILED(hr)) {
23760 /* 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. */
23761 if (hr == E_ACCESSDENIED) {
23762 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED;
23763 } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
23764 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY;
23765 } else {
23766 errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr);
23767 }
23768 goto done;
23769 }
23770 }
23771
23772 if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) {
23773 /*
23774 Low latency shared mode via IAudioClient3.
23775
23776 NOTE
23777 ====
23778 Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the
23779 use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using
23780 any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to
23781 that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM.
23782 */
23783 #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE
23784 {
23785 if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.nSamplesPerSec) {
23786 ma_IAudioClient3* pAudioClient3 = NULL;
23787 hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3);
23788 if (SUCCEEDED(hr)) {
23789 ma_uint32 defaultPeriodInFrames;
23790 ma_uint32 fundamentalPeriodInFrames;
23791 ma_uint32 minPeriodInFrames;
23792 ma_uint32 maxPeriodInFrames;
23793 hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (MA_WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames);
23794 if (SUCCEEDED(hr)) {
23795 ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut;
23796 ma_uint32 actualPeriodInFrames = desiredPeriodInFrames;
23797
23798 /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */
23799 actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames;
23800 actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames;
23801
23802 /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */
23803 actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames);
23804
23805 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames);
23806 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames);
23807 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames);
23808 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames);
23809 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames);
23810
23811 /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */
23812 if (actualPeriodInFrames >= desiredPeriodInFrames) {
23813 /*
23814 MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified,
23815 IAudioClient3_InitializeSharedAudioStream() will fail.
23816 */
23817 hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (MA_WAVEFORMATEX*)&wf, NULL);
23818 if (SUCCEEDED(hr)) {
23819 wasInitializedUsingIAudioClient3 = MA_TRUE;
23820 pData->periodSizeInFramesOut = actualPeriodInFrames;
23821
23822 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n");
23823 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut);
23824 } else {
23825 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n");
23826 }
23827 } else {
23828 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");
23829 }
23830 } else {
23831 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n");
23832 }
23833
23834 ma_IAudioClient3_Release(pAudioClient3);
23835 pAudioClient3 = NULL;
23836 }
23837 }
23838 }
23839 #else
23840 {
23841 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");
23842 }
23843 #endif
23844
23845 /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */
23846 if (!wasInitializedUsingIAudioClient3) {
23847 MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */
23848 hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (const MA_WAVEFORMATEX*)&wf, NULL);
23849 if (FAILED(hr)) {
23850 if (hr == E_ACCESSDENIED) {
23851 errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED;
23852 } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
23853 errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY;
23854 } else {
23855 errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr);
23856 }
23857
23858 goto done;
23859 }
23860 }
23861 }
23862
23863 if (!wasInitializedUsingIAudioClient3) {
23864 ma_uint32 bufferSizeInFrames = 0;
23865 hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
23866 if (FAILED(hr)) {
23867 errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr);
23868 goto done;
23869 }
23870
23871 /*
23872 When using process loopback mode, retrieval of the buffer size seems to result in totally
23873 incorrect values. In this case we'll just assume it's the same size as what we requested
23874 when we initialized the client.
23875 */
23876 if (usingProcessLoopback) {
23877 bufferSizeInFrames = (ma_uint32)((periodDurationInMicroseconds * pData->periodsOut) * pData->sampleRateOut / 1000000);
23878 }
23879
23880 pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut;
23881 }
23882
23883 pData->usingAudioClient3 = wasInitializedUsingIAudioClient3;
23884
23885
23886 if (deviceType == ma_device_type_playback) {
23887 result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient);
23888 } else {
23889 result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient);
23890 }
23891
23892 /*if (FAILED(hr)) {*/
23893 if (result != MA_SUCCESS) {
23894 errorMsg = "[WASAPI] Failed to get audio client service.";
23895 goto done;
23896 }
23897
23898
23899 /* Grab the name of the device. */
23900 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
23901 {
23902 ma_IPropertyStore *pProperties;
23903 hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties);
23904 if (SUCCEEDED(hr)) {
23905 MA_PROPVARIANT varName;
23906 ma_PropVariantInit(&varName);
23907 hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName);
23908 if (SUCCEEDED(hr)) {
23909 WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE);
23910 ma_PropVariantClear(pContext, &varName);
23911 }
23912
23913 ma_IPropertyStore_Release(pProperties);
23914 }
23915 }
23916 #endif
23917
23918 /*
23919 For the WASAPI backend we need to know the actual IDs of the device in order to do automatic
23920 stream routing so that IDs can be compared and we can determine which device has been detached
23921 and whether or not it matches with our ma_device.
23922 */
23923 #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
23924 {
23925 /* Desktop */
23926 ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id);
23927 }
23928 #else
23929 {
23930 /* UWP */
23931 /* TODO: Implement me. Need to figure out how to get the ID of the default device. */
23932 }
23933 #endif
23934
23935done:
23936 /* Clean up. */
23937#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
23938 if (pDeviceInterface != NULL) {
23939 ma_IMMDevice_Release(pDeviceInterface);
23940 }
23941#else
23942 if (pDeviceInterface != NULL) {
23943 ma_IUnknown_Release(pDeviceInterface);
23944 }
23945#endif
23946
23947 if (result != MA_SUCCESS) {
23948 if (pData->pRenderClient) {
23949 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient);
23950 pData->pRenderClient = NULL;
23951 }
23952 if (pData->pCaptureClient) {
23953 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient);
23954 pData->pCaptureClient = NULL;
23955 }
23956 if (pData->pAudioClient) {
23957 ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
23958 pData->pAudioClient = NULL;
23959 }
23960
23961 if (errorMsg != NULL && errorMsg[0] != '\0') {
23962 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s\n", errorMsg);
23963 }
23964
23965 return result;
23966 } else {
23967 return MA_SUCCESS;
23968 }
23969}
23970
23971static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType)
23972{
23973 ma_device_init_internal_data__wasapi data;
23974 ma_result result;
23975
23976 MA_ASSERT(pDevice != NULL);
23977
23978 /* We only re-initialize the playback or capture device. Never a full-duplex device. */
23979 if (deviceType == ma_device_type_duplex) {
23980 return MA_INVALID_ARGS;
23981 }
23982
23983
23984 /*
23985 Before reinitializing the device we need to free the previous audio clients.
23986
23987 There's a known memory leak here. We will be calling this from the routing change callback that
23988 is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion
23989 this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably
23990 need some system where we post an event, but delay the execution of it until the callback has
23991 returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for
23992 a command thread which might be useful for this.
23993 */
23994 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
23995 if (pDevice->wasapi.pCaptureClient) {
23996 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
23997 pDevice->wasapi.pCaptureClient = NULL;
23998 }
23999
24000 if (pDevice->wasapi.pAudioClientCapture) {
24001 /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/
24002 pDevice->wasapi.pAudioClientCapture = NULL;
24003 }
24004 }
24005
24006 if (deviceType == ma_device_type_playback) {
24007 if (pDevice->wasapi.pRenderClient) {
24008 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
24009 pDevice->wasapi.pRenderClient = NULL;
24010 }
24011
24012 if (pDevice->wasapi.pAudioClientPlayback) {
24013 /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/
24014 pDevice->wasapi.pAudioClientPlayback = NULL;
24015 }
24016 }
24017
24018
24019 if (deviceType == ma_device_type_playback) {
24020 data.formatIn = pDevice->playback.format;
24021 data.channelsIn = pDevice->playback.channels;
24022 MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
24023 data.shareMode = pDevice->playback.shareMode;
24024 } else {
24025 data.formatIn = pDevice->capture.format;
24026 data.channelsIn = pDevice->capture.channels;
24027 MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
24028 data.shareMode = pDevice->capture.shareMode;
24029 }
24030
24031 data.sampleRateIn = pDevice->sampleRate;
24032 data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames;
24033 data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds;
24034 data.periodsIn = pDevice->wasapi.originalPeriods;
24035 data.performanceProfile = pDevice->wasapi.originalPerformanceProfile;
24036 data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC;
24037 data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC;
24038 data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading;
24039 data.loopbackProcessID = pDevice->wasapi.loopbackProcessID;
24040 data.loopbackProcessExclude = pDevice->wasapi.loopbackProcessExclude;
24041 result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data);
24042 if (result != MA_SUCCESS) {
24043 return result;
24044 }
24045
24046 /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */
24047 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
24048 pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
24049 pDevice->wasapi.pCaptureClient = data.pCaptureClient;
24050
24051 pDevice->capture.internalFormat = data.formatOut;
24052 pDevice->capture.internalChannels = data.channelsOut;
24053 pDevice->capture.internalSampleRate = data.sampleRateOut;
24054 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
24055 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
24056 pDevice->capture.internalPeriods = data.periodsOut;
24057 ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName);
24058
24059 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture);
24060
24061 pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
24062 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
24063
24064 /* We must always have a valid ID. */
24065 ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);
24066 }
24067
24068 if (deviceType == ma_device_type_playback) {
24069 pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
24070 pDevice->wasapi.pRenderClient = data.pRenderClient;
24071
24072 pDevice->playback.internalFormat = data.formatOut;
24073 pDevice->playback.internalChannels = data.channelsOut;
24074 pDevice->playback.internalSampleRate = data.sampleRateOut;
24075 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
24076 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
24077 pDevice->playback.internalPeriods = data.periodsOut;
24078 ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName);
24079
24080 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback);
24081
24082 pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
24083 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
24084
24085 /* We must always have a valid ID because rerouting will look at it. */
24086 ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);
24087 }
24088
24089 return MA_SUCCESS;
24090}
24091
24092static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
24093{
24094 ma_result result = MA_SUCCESS;
24095
24096#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
24097 HRESULT hr;
24098 ma_IMMDeviceEnumerator* pDeviceEnumerator;
24099#endif
24100
24101 MA_ASSERT(pDevice != NULL);
24102
24103 MA_ZERO_OBJECT(&pDevice->wasapi);
24104 pDevice->wasapi.usage = pConfig->wasapi.usage;
24105 pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
24106 pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
24107 pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
24108 pDevice->wasapi.loopbackProcessID = pConfig->wasapi.loopbackProcessID;
24109 pDevice->wasapi.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude;
24110
24111 /* Exclusive mode is not allowed with loopback. */
24114 }
24115
24117 ma_device_init_internal_data__wasapi data;
24118 data.formatIn = pDescriptorCapture->format;
24119 data.channelsIn = pDescriptorCapture->channels;
24120 data.sampleRateIn = pDescriptorCapture->sampleRate;
24121 MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
24122 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
24123 data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds;
24124 data.periodsIn = pDescriptorCapture->periodCount;
24125 data.shareMode = pDescriptorCapture->shareMode;
24126 data.performanceProfile = pConfig->performanceProfile;
24127 data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
24128 data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
24129 data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
24130 data.loopbackProcessID = pConfig->wasapi.loopbackProcessID;
24131 data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude;
24132
24133 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);
24134 if (result != MA_SUCCESS) {
24135 return result;
24136 }
24137
24138 pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
24139 pDevice->wasapi.pCaptureClient = data.pCaptureClient;
24140 pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;
24141 pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
24142 pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount;
24143 pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile;
24144
24145 /*
24146 The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled,
24147 however, because we want to block until we actually have something for the first call to ma_device_read().
24148 */
24149 pDevice->wasapi.hEventCapture = (ma_handle)CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */
24150 if (pDevice->wasapi.hEventCapture == NULL) {
24151 result = ma_result_from_GetLastError(GetLastError());
24152
24153 if (pDevice->wasapi.pCaptureClient != NULL) {
24154 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
24155 pDevice->wasapi.pCaptureClient = NULL;
24156 }
24157 if (pDevice->wasapi.pAudioClientCapture != NULL) {
24158 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
24159 pDevice->wasapi.pAudioClientCapture = NULL;
24160 }
24161
24162 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture.");
24163 return result;
24164 }
24165 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture);
24166
24167 pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
24168 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
24169
24170 /* We must always have a valid ID. */
24171 ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);
24172
24173 /* The descriptor needs to be updated with actual values. */
24174 pDescriptorCapture->format = data.formatOut;
24175 pDescriptorCapture->channels = data.channelsOut;
24176 pDescriptorCapture->sampleRate = data.sampleRateOut;
24177 MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
24178 pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut;
24179 pDescriptorCapture->periodCount = data.periodsOut;
24180 }
24181
24182 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
24183 ma_device_init_internal_data__wasapi data;
24184 data.formatIn = pDescriptorPlayback->format;
24185 data.channelsIn = pDescriptorPlayback->channels;
24186 data.sampleRateIn = pDescriptorPlayback->sampleRate;
24187 MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
24188 data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames;
24189 data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
24190 data.periodsIn = pDescriptorPlayback->periodCount;
24191 data.shareMode = pDescriptorPlayback->shareMode;
24192 data.performanceProfile = pConfig->performanceProfile;
24193 data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
24194 data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
24195 data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
24196 data.loopbackProcessID = pConfig->wasapi.loopbackProcessID;
24197 data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude;
24198
24199 result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data);
24200 if (result != MA_SUCCESS) {
24201 if (pConfig->deviceType == ma_device_type_duplex) {
24202 if (pDevice->wasapi.pCaptureClient != NULL) {
24203 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
24204 pDevice->wasapi.pCaptureClient = NULL;
24205 }
24206 if (pDevice->wasapi.pAudioClientCapture != NULL) {
24207 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
24208 pDevice->wasapi.pAudioClientCapture = NULL;
24209 }
24210
24211 CloseHandle((HANDLE)pDevice->wasapi.hEventCapture);
24212 pDevice->wasapi.hEventCapture = NULL;
24213 }
24214 return result;
24215 }
24216
24217 pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
24218 pDevice->wasapi.pRenderClient = data.pRenderClient;
24219 pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
24220 pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
24221 pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount;
24222 pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile;
24223
24224 /*
24225 The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled
24226 only after the whole available space has been filled, never before.
24227
24228 The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able
24229 to get passed WaitForMultipleObjects().
24230 */
24231 pDevice->wasapi.hEventPlayback = (ma_handle)CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */
24232 if (pDevice->wasapi.hEventPlayback == NULL) {
24233 result = ma_result_from_GetLastError(GetLastError());
24234
24235 if (pConfig->deviceType == ma_device_type_duplex) {
24236 if (pDevice->wasapi.pCaptureClient != NULL) {
24237 ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
24238 pDevice->wasapi.pCaptureClient = NULL;
24239 }
24240 if (pDevice->wasapi.pAudioClientCapture != NULL) {
24241 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
24242 pDevice->wasapi.pAudioClientCapture = NULL;
24243 }
24244
24245 CloseHandle((HANDLE)pDevice->wasapi.hEventCapture);
24246 pDevice->wasapi.hEventCapture = NULL;
24247 }
24248
24249 if (pDevice->wasapi.pRenderClient != NULL) {
24250 ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
24251 pDevice->wasapi.pRenderClient = NULL;
24252 }
24253 if (pDevice->wasapi.pAudioClientPlayback != NULL) {
24254 ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
24255 pDevice->wasapi.pAudioClientPlayback = NULL;
24256 }
24257
24258 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback.");
24259 return result;
24260 }
24261 ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback);
24262
24263 pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
24264 ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
24265
24266 /* We must always have a valid ID because rerouting will look at it. */
24267 ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);
24268
24269 /* The descriptor needs to be updated with actual values. */
24270 pDescriptorPlayback->format = data.formatOut;
24271 pDescriptorPlayback->channels = data.channelsOut;
24272 pDescriptorPlayback->sampleRate = data.sampleRateOut;
24273 MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
24274 pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut;
24275 pDescriptorPlayback->periodCount = data.periodsOut;
24276 }
24277
24278 /*
24279 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
24280 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
24281 stop the device outright and let the application handle it.
24282 */
24283#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
24284 if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) {
24285 if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) && pConfig->capture.pDeviceID == NULL) {
24286 pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE;
24287 }
24288 if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) {
24289 pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE;
24290 }
24291 }
24292
24293 ma_mutex_init(&pDevice->wasapi.rerouteLock);
24294
24295 hr = ma_CoCreateInstance(pDevice->pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
24296 if (FAILED(hr)) {
24297 ma_device_uninit__wasapi(pDevice);
24298 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
24299 return ma_result_from_HRESULT(hr);
24300 }
24301
24302 pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl;
24303 pDevice->wasapi.notificationClient.counter = 1;
24304 pDevice->wasapi.notificationClient.pDevice = pDevice;
24305
24306 hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient);
24307 if (SUCCEEDED(hr)) {
24308 pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator;
24309 } else {
24310 /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */
24311 ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
24312 }
24313#endif
24314
24315 ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE);
24316 ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
24317
24318 return MA_SUCCESS;
24319}
24320
24321static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount)
24322{
24323 ma_uint32 paddingFramesCount;
24324 HRESULT hr;
24325 ma_share_mode shareMode;
24326
24327 MA_ASSERT(pDevice != NULL);
24328 MA_ASSERT(pFrameCount != NULL);
24329
24330 *pFrameCount = 0;
24331
24332 if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) {
24333 return MA_INVALID_OPERATION;
24334 }
24335
24336 /*
24337 I've had a report that GetCurrentPadding() is returning a frame count of 0 which is preventing
24338 higher level function calls from doing anything because it thinks nothing is available. I have
24339 taken a look at the documentation and it looks like this is unnecessary in exclusive mode.
24340
24341 From Microsoft's documentation:
24342
24343 For an exclusive-mode rendering or capture stream that was initialized with the
24344 AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag, the client typically has no use for the padding
24345 value reported by GetCurrentPadding. Instead, the client accesses an entire buffer during
24346 each processing pass.
24347
24348 Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the
24349 entire buffer. This depends on the caller making sure they wait on the event handler.
24350 */
24351 shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode;
24352 if (shareMode == ma_share_mode_shared) {
24353 /* Shared mode. */
24354 hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount);
24355 if (FAILED(hr)) {
24356 return ma_result_from_HRESULT(hr);
24357 }
24358
24359 if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
24360 *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount;
24361 } else {
24362 *pFrameCount = paddingFramesCount;
24363 }
24364 } else {
24365 /* Exclusive mode. */
24366 if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
24367 *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback;
24368 } else {
24369 *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture;
24370 }
24371 }
24372
24373 return MA_SUCCESS;
24374}
24375
24376
24377static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType)
24378{
24379 ma_result result;
24380
24381 if (deviceType == ma_device_type_duplex) {
24382 return MA_INVALID_ARGS;
24383 }
24384
24385 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== CHANGING DEVICE ===\n");
24386
24387 result = ma_device_reinit__wasapi(pDevice, deviceType);
24388 if (result != MA_SUCCESS) {
24389 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WASAPI] Reinitializing device after route change failed.\n");
24390 return result;
24391 }
24392
24393 ma_device__post_init_setup(pDevice, deviceType);
24394 ma_device__on_notification_rerouted(pDevice);
24395
24396 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== DEVICE CHANGED ===\n");
24397
24398 return MA_SUCCESS;
24399}
24400
24401static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice)
24402{
24403 HRESULT hr;
24404
24405 if (pDevice->pContext->wasapi.hAvrt) {
24406 const char* pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage);
24407 if (pTaskName) {
24408 DWORD idx = 0;
24409 pDevice->wasapi.hAvrtHandle = (ma_handle)((MA_PFN_AvSetMmThreadCharacteristicsA)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsA)(pTaskName, &idx);
24410 }
24411 }
24412
24413 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
24414 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
24415 if (FAILED(hr)) {
24416 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device. HRESULT = %d.", (int)hr);
24417 return ma_result_from_HRESULT(hr);
24418 }
24419
24420 ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_TRUE);
24421 }
24422
24423 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24424 hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
24425 if (FAILED(hr)) {
24426 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device. HRESULT = %d.", (int)hr);
24427 return ma_result_from_HRESULT(hr);
24428 }
24429
24430 ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
24431 }
24432
24433 return MA_SUCCESS;
24434}
24435
24436static ma_result ma_device_start__wasapi(ma_device* pDevice)
24437{
24438 ma_result result;
24439
24440 MA_ASSERT(pDevice != NULL);
24441
24442 /* Wait for any rerouting to finish before attempting to start the device. */
24443 ma_mutex_lock(&pDevice->wasapi.rerouteLock);
24444 {
24445 result = ma_device_start__wasapi_nolock(pDevice);
24446 }
24447 ma_mutex_unlock(&pDevice->wasapi.rerouteLock);
24448
24449 return result;
24450}
24451
24452static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice)
24453{
24454 ma_result result;
24455 HRESULT hr;
24456
24457 MA_ASSERT(pDevice != NULL);
24458
24459 if (pDevice->wasapi.hAvrtHandle) {
24460 ((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle);
24461 pDevice->wasapi.hAvrtHandle = NULL;
24462 }
24463
24464 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
24465 /* If we have a mapped buffer we need to release it. */
24466 if (pDevice->wasapi.pMappedBufferCapture != NULL) {
24467 ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
24468 pDevice->wasapi.pMappedBufferCapture = NULL;
24469 pDevice->wasapi.mappedBufferCaptureCap = 0;
24470 pDevice->wasapi.mappedBufferCaptureLen = 0;
24471 }
24472
24473 hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
24474 if (FAILED(hr)) {
24475 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device.");
24476 return ma_result_from_HRESULT(hr);
24477 }
24478
24479 /* The audio client needs to be reset otherwise restarting will fail. */
24480 hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
24481 if (FAILED(hr)) {
24482 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device.");
24483 return ma_result_from_HRESULT(hr);
24484 }
24485
24486 ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE);
24487 }
24488
24489 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24490 if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
24492 ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels),
24493 pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen,
24495 );
24496 ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
24497 pDevice->wasapi.pMappedBufferPlayback = NULL;
24498 pDevice->wasapi.mappedBufferPlaybackCap = 0;
24499 pDevice->wasapi.mappedBufferPlaybackLen = 0;
24500 }
24501
24502 /*
24503 The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to
24504 the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played.
24505 */
24506 if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) {
24507 /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */
24508 DWORD waitTime = (pDevice->wasapi.actualBufferSizeInFramesPlayback * 1000) / pDevice->playback.internalSampleRate;
24509
24510 if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
24511 WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime);
24512 } else {
24513 ma_uint32 prevFramesAvailablePlayback = (ma_uint32)-1;
24514 ma_uint32 framesAvailablePlayback;
24515 for (;;) {
24516 result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
24517 if (result != MA_SUCCESS) {
24518 break;
24519 }
24520
24521 if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) {
24522 break;
24523 }
24524
24525 /*
24526 Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames
24527 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.
24528 */
24529 if (framesAvailablePlayback == prevFramesAvailablePlayback) {
24530 break;
24531 }
24532 prevFramesAvailablePlayback = framesAvailablePlayback;
24533
24534 ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */
24535 WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime);
24536 }
24537 }
24538 }
24539
24540 hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
24541 if (FAILED(hr)) {
24542 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device.");
24543 return ma_result_from_HRESULT(hr);
24544 }
24545
24546 /* The audio client needs to be reset otherwise restarting will fail. */
24547 {
24548 ma_int32 retries = 5;
24549
24550 while ((hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback)) == MA_AUDCLNT_E_BUFFER_OPERATION_PENDING && retries > 0) {
24551 ma_sleep(10);
24552 retries -= 1;
24553 }
24554 }
24555
24556 if (FAILED(hr)) {
24557 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device.");
24558 return ma_result_from_HRESULT(hr);
24559 }
24560
24561 ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
24562 }
24563
24564 return MA_SUCCESS;
24565}
24566
24567static ma_result ma_device_stop__wasapi(ma_device* pDevice)
24568{
24569 ma_result result;
24570
24571 MA_ASSERT(pDevice != NULL);
24572
24573 /* Wait for any rerouting to finish before attempting to stop the device. */
24574 ma_mutex_lock(&pDevice->wasapi.rerouteLock);
24575 {
24576 result = ma_device_stop__wasapi_nolock(pDevice);
24577 }
24578 ma_mutex_unlock(&pDevice->wasapi.rerouteLock);
24579
24580 return result;
24581}
24582
24583
24584#ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS
24585#define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000
24586#endif
24587
24588static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
24589{
24590 ma_result result = MA_SUCCESS;
24591 ma_uint32 totalFramesProcessed = 0;
24592
24593 /*
24594 When reading, we need to get a buffer and process all of it before releasing it. Because the
24595 frame count (frameCount) can be different to the size of the buffer, we'll need to cache the
24596 pointer to the buffer.
24597 */
24598
24599 /* Keep running until we've processed the requested number of frames. */
24600 while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) {
24601 ma_uint32 framesRemaining = frameCount - totalFramesProcessed;
24602
24603 /* If we have a mapped data buffer, consume that first. */
24604 if (pDevice->wasapi.pMappedBufferCapture != NULL) {
24605 /* We have a cached data pointer so consume that before grabbing another one from WASAPI. */
24606 ma_uint32 framesToProcessNow = framesRemaining;
24607 if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) {
24608 framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen;
24609 }
24610
24611 /* Now just copy the data over to the output buffer. */
24613 ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels),
24614 ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels),
24615 framesToProcessNow,
24617 );
24618
24619 totalFramesProcessed += framesToProcessNow;
24620 pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow;
24621
24622 /* If the data buffer has been fully consumed we need to release it. */
24623 if (pDevice->wasapi.mappedBufferCaptureLen == 0) {
24624 ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
24625 pDevice->wasapi.pMappedBufferCapture = NULL;
24626 pDevice->wasapi.mappedBufferCaptureCap = 0;
24627 }
24628 } else {
24629 /* We don't have any cached data pointer, so grab another one. */
24630 HRESULT hr;
24631 DWORD flags = 0;
24632
24633 /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */
24634 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL);
24635 if (hr == S_OK) {
24636 /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */
24637 pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap;
24638
24639 /*
24640 There have been reports that indicate that at times the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY is reported for every
24641 call to IAudioCaptureClient_GetBuffer() above which results in spamming of the debug messages below. To partially
24642 work around this, I'm only outputting these messages when MA_DEBUG_OUTPUT is explicitly defined. The better solution
24643 would be to figure out why the flag is always getting reported.
24644 */
24645 #if defined(MA_DEBUG_OUTPUT)
24646 {
24647 if (flags != 0) {
24648 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags);
24649
24650 if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
24651 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);
24652 }
24653 }
24654 }
24655 #endif
24656
24657 /* Overrun detection. */
24658 if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
24659 /* Glitched. Probably due to an overrun. */
24660
24661 /*
24662 If we got an overrun it probably means we're straddling the end of the buffer. In normal capture
24663 mode this is the fault of the client application because they're responsible for ensuring data is
24664 processed fast enough. In duplex mode, however, the processing of audio is tied to the playback
24665 device, so this can possibly be the result of a timing de-sync.
24666
24667 In capture mode we're not going to do any kind of recovery because the real fix is for the client
24668 application to process faster. In duplex mode, we'll treat this as a desync and reset the buffers
24669 to prevent a never-ending sequence of glitches due to straddling the end of the buffer.
24670 */
24671 if (pDevice->type == ma_device_type_duplex) {
24672 /*
24673 Experiment:
24674
24675 If we empty out the *entire* buffer we may end up putting ourselves into an underrun position
24676 which isn't really any better than the overrun we're probably in right now. Instead we'll just
24677 empty out about half.
24678 */
24679 ma_uint32 i;
24680 ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture);
24681 ma_uint32 iterationCount = periodCount / 2;
24682 if ((periodCount % 2) > 0) {
24683 iterationCount += 1;
24684 }
24685
24686 for (i = 0; i < iterationCount; i += 1) {
24687 hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
24688 if (FAILED(hr)) {
24689 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %ld.\n", hr);
24690 break;
24691 }
24692
24693 flags = 0;
24694 hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL);
24695 if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) {
24696 /*
24697 The buffer has been completely emptied or an error occurred. In this case we'll need
24698 to reset the state of the mapped buffer which will trigger the next iteration to get
24699 a fresh buffer from WASAPI.
24700 */
24701 pDevice->wasapi.pMappedBufferCapture = NULL;
24702 pDevice->wasapi.mappedBufferCaptureCap = 0;
24703 pDevice->wasapi.mappedBufferCaptureLen = 0;
24704
24705 if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) {
24706 if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
24707 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied, and data discontinuity still reported.\n");
24708 } else {
24709 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied.\n");
24710 }
24711 }
24712
24713 if (FAILED(hr)) {
24714 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %ld.\n", hr);
24715 }
24716
24717 break;
24718 }
24719 }
24720
24721 /* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */
24722 if (pDevice->wasapi.pMappedBufferCapture != NULL) {
24723 pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap;
24724 }
24725 }
24726 }
24727
24728 continue;
24729 } else {
24730 if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || hr == MA_AUDCLNT_E_BUFFER_ERROR) {
24731 /*
24732 No data is available. We need to wait for more. There's two situations to consider
24733 here. The first is normal capture mode. If this times out it probably means the
24734 microphone isn't delivering data for whatever reason. In this case we'll just
24735 abort the read and return whatever we were able to get. The other situations is
24736 loopback mode, in which case a timeout probably just means the nothing is playing
24737 through the speakers.
24738 */
24739
24740 /* Experiment: Use a shorter timeout for loopback mode. */
24741 DWORD timeoutInMilliseconds = MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS;
24742 if (pDevice->type == ma_device_type_loopback) {
24743 timeoutInMilliseconds = 10;
24744 }
24745
24746 if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) {
24747 if (pDevice->type == ma_device_type_loopback) {
24748 continue; /* Keep waiting in loopback mode. */
24749 } else {
24750 result = MA_ERROR;
24751 break; /* Wait failed. */
24752 }
24753 }
24754
24755 /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */
24756 } else {
24757 /* An error occurred and we need to abort. */
24758 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);
24759 result = ma_result_from_HRESULT(hr);
24760 break;
24761 }
24762 }
24763 }
24764 }
24765
24766 /*
24767 If we were unable to process the entire requested frame count, but we still have a mapped buffer,
24768 there's a good chance either an error occurred or the device was stopped mid-read. In this case
24769 we'll need to make sure the buffer is released.
24770 */
24771 if (totalFramesProcessed < frameCount && pDevice->wasapi.pMappedBufferCapture != NULL) {
24772 ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
24773 pDevice->wasapi.pMappedBufferCapture = NULL;
24774 pDevice->wasapi.mappedBufferCaptureCap = 0;
24775 pDevice->wasapi.mappedBufferCaptureLen = 0;
24776 }
24777
24778 if (pFramesRead != NULL) {
24779 *pFramesRead = totalFramesProcessed;
24780 }
24781
24782 return result;
24783}
24784
24785static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
24786{
24787 ma_result result = MA_SUCCESS;
24788 ma_uint32 totalFramesProcessed = 0;
24789
24790 /* Keep writing to the device until it's stopped or we've consumed all of our input. */
24791 while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) {
24792 ma_uint32 framesRemaining = frameCount - totalFramesProcessed;
24793
24794 /*
24795 We're going to do this in a similar way to capture. We'll first check if the cached data pointer
24796 is valid, and if so, read from that. Otherwise We will call IAudioRenderClient_GetBuffer() with
24797 a requested buffer size equal to our actual period size. If it returns AUDCLNT_E_BUFFER_TOO_LARGE
24798 it means we need to wait for some data to become available.
24799 */
24800 if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
24801 /* We still have some space available in the mapped data buffer. Write to it. */
24802 ma_uint32 framesToProcessNow = framesRemaining;
24803 if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) {
24804 framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen);
24805 }
24806
24807 /* Now just copy the data over to the output buffer. */
24809 ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels),
24810 ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels),
24811 framesToProcessNow,
24813 );
24814
24815 totalFramesProcessed += framesToProcessNow;
24816 pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow;
24817
24818 /* If the data buffer has been fully consumed we need to release it. */
24819 if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) {
24820 ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
24821 pDevice->wasapi.pMappedBufferPlayback = NULL;
24822 pDevice->wasapi.mappedBufferPlaybackCap = 0;
24823 pDevice->wasapi.mappedBufferPlaybackLen = 0;
24824
24825 /*
24826 In exclusive mode we need to wait here. Exclusive mode is weird because GetBuffer() never
24827 seems to return AUDCLNT_E_BUFFER_TOO_LARGE, which is what we normally use to determine
24828 whether or not we need to wait for more data.
24829 */
24830 if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
24831 if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
24832 result = MA_ERROR;
24833 break; /* Wait failed. Probably timed out. */
24834 }
24835 }
24836 }
24837 } else {
24838 /* We don't have a mapped data buffer so we'll need to get one. */
24839 HRESULT hr;
24840 ma_uint32 bufferSizeInFrames;
24841
24842 /* Special rules for exclusive mode. */
24843 if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
24844 bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback;
24845 } else {
24846 bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback;
24847 }
24848
24849 hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback);
24850 if (hr == S_OK) {
24851 /* We have data available. */
24852 pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames;
24853 pDevice->wasapi.mappedBufferPlaybackLen = 0;
24854 } else {
24855 if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) {
24856 /* Not enough data available. We need to wait for more. */
24857 if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
24858 result = MA_ERROR;
24859 break; /* Wait failed. Probably timed out. */
24860 }
24861 } else {
24862 /* Some error occurred. We'll need to abort. */
24863 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);
24864 result = ma_result_from_HRESULT(hr);
24865 break;
24866 }
24867 }
24868 }
24869 }
24870
24871 if (pFramesWritten != NULL) {
24872 *pFramesWritten = totalFramesProcessed;
24873 }
24874
24875 return result;
24876}
24877
24878static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice)
24879{
24880 MA_ASSERT(pDevice != NULL);
24881
24882 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
24883 SetEvent((HANDLE)pDevice->wasapi.hEventCapture);
24884 }
24885
24886 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24887 SetEvent((HANDLE)pDevice->wasapi.hEventPlayback);
24888 }
24889
24890 return MA_SUCCESS;
24891}
24892
24893
24894static ma_result ma_context_uninit__wasapi(ma_context* pContext)
24895{
24896 ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI);
24897
24898 MA_ASSERT(pContext != NULL);
24899 MA_ASSERT(pContext->backend == ma_backend_wasapi);
24900
24901 ma_context_post_command__wasapi(pContext, &cmd);
24902 ma_thread_wait(&pContext->wasapi.commandThread);
24903
24904 if (pContext->wasapi.hAvrt) {
24905 ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt);
24906 pContext->wasapi.hAvrt = NULL;
24907 }
24908
24909 #if defined(MA_WIN32_UWP)
24910 {
24911 if (pContext->wasapi.hMMDevapi) {
24912 ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi);
24913 pContext->wasapi.hMMDevapi = NULL;
24914 }
24915 }
24916 #endif
24917
24918 /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */
24919 ma_semaphore_uninit(&pContext->wasapi.commandSem);
24920 ma_mutex_uninit(&pContext->wasapi.commandLock);
24921
24922 return MA_SUCCESS;
24923}
24924
24925static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
24926{
24927 ma_result result = MA_SUCCESS;
24928
24929 MA_ASSERT(pContext != NULL);
24930
24931 (void)pConfig;
24932
24933#ifdef MA_WIN32_DESKTOP
24934 /*
24935 WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven
24936 exclusive mode does not work until SP1.
24937
24938 Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error.
24939 */
24940 {
24941 ma_OSVERSIONINFOEXW osvi;
24942 ma_handle kernel32DLL;
24943 ma_PFNVerifyVersionInfoW _VerifyVersionInfoW;
24944 ma_PFNVerSetConditionMask _VerSetConditionMask;
24945
24946 kernel32DLL = ma_dlopen(ma_context_get_log(pContext), "kernel32.dll");
24947 if (kernel32DLL == NULL) {
24948 return MA_NO_BACKEND;
24949 }
24950
24951 _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerifyVersionInfoW");
24952 _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerSetConditionMask");
24953 if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) {
24954 ma_dlclose(ma_context_get_log(pContext), kernel32DLL);
24955 return MA_NO_BACKEND;
24956 }
24957
24958 MA_ZERO_OBJECT(&osvi);
24959 osvi.dwOSVersionInfoSize = sizeof(osvi);
24960 osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF);
24961 osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF);
24962 osvi.wServicePackMajor = 1;
24963 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))) {
24964 result = MA_SUCCESS;
24965 } else {
24966 result = MA_NO_BACKEND;
24967 }
24968
24969 ma_dlclose(ma_context_get_log(pContext), kernel32DLL);
24970 }
24971#endif
24972
24973 if (result != MA_SUCCESS) {
24974 return result;
24975 }
24976
24977 MA_ZERO_OBJECT(&pContext->wasapi);
24978
24979
24980 #if defined(MA_WIN32_UWP)
24981 {
24982 /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */
24983 pContext->wasapi.hMMDevapi = ma_dlopen(ma_context_get_log(pContext), "mmdevapi.dll");
24984 if (pContext->wasapi.hMMDevapi) {
24985 pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync");
24986 if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) {
24987 ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi);
24988 return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */
24989 }
24990 } else {
24991 return MA_NO_BACKEND; /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */
24992 }
24993 }
24994 #endif
24995
24996 /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */
24997 pContext->wasapi.hAvrt = ma_dlopen(ma_context_get_log(pContext), "avrt.dll");
24998 if (pContext->wasapi.hAvrt) {
24999 pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA");
25000 pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics");
25001
25002 /* If either function could not be found, disable use of avrt entirely. */
25003 if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) {
25004 pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL;
25005 pContext->wasapi.AvRevertMmThreadcharacteristics = NULL;
25006 ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt);
25007 pContext->wasapi.hAvrt = NULL;
25008 }
25009 }
25010
25011
25012 /*
25013 Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread
25014 than the one that retrieved it with GetService(). This can result in a deadlock in two
25015 situations:
25016
25017 1) When calling ma_device_uninit() from a different thread to ma_device_init(); and
25018 2) When uninitializing and reinitializing the internal IAudioClient object in response to
25019 automatic stream routing.
25020
25021 We could define ma_device_uninit() such that it must be called on the same thread as
25022 ma_device_init(). We could also just not release the IAudioClient when performing automatic
25023 stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so
25024 we're going to have to work around this with a worker thread. This is not ideal, but I can't
25025 think of a better way to do this.
25026
25027 More information about this can be found here:
25028
25029 https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient
25030
25031 Note this section:
25032
25033 When releasing an IAudioRenderClient interface instance, the client must call the interface's
25034 Release method from the same thread as the call to IAudioClient::GetService that created the
25035 object.
25036 */
25037 {
25038 result = ma_mutex_init(&pContext->wasapi.commandLock);
25039 if (result != MA_SUCCESS) {
25040 return result;
25041 }
25042
25043 result = ma_semaphore_init(0, &pContext->wasapi.commandSem);
25044 if (result != MA_SUCCESS) {
25045 ma_mutex_uninit(&pContext->wasapi.commandLock);
25046 return result;
25047 }
25048
25049 result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks);
25050 if (result != MA_SUCCESS) {
25051 ma_semaphore_uninit(&pContext->wasapi.commandSem);
25052 ma_mutex_uninit(&pContext->wasapi.commandLock);
25053 return result;
25054 }
25055 }
25056
25057
25058 pCallbacks->onContextInit = ma_context_init__wasapi;
25059 pCallbacks->onContextUninit = ma_context_uninit__wasapi;
25060 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi;
25061 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi;
25062 pCallbacks->onDeviceInit = ma_device_init__wasapi;
25063 pCallbacks->onDeviceUninit = ma_device_uninit__wasapi;
25064 pCallbacks->onDeviceStart = ma_device_start__wasapi;
25065 pCallbacks->onDeviceStop = ma_device_stop__wasapi;
25066 pCallbacks->onDeviceRead = ma_device_read__wasapi;
25067 pCallbacks->onDeviceWrite = ma_device_write__wasapi;
25068 pCallbacks->onDeviceDataLoop = NULL;
25069 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi;
25070
25071 return MA_SUCCESS;
25072}
25073#endif
25074
25075/******************************************************************************
25076
25077DirectSound Backend
25078
25079******************************************************************************/
25080#ifdef MA_HAS_DSOUND
25081/*#include <dsound.h>*/
25082
25083/*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/
25084
25085/* miniaudio only uses priority or exclusive modes. */
25086#define MA_DSSCL_NORMAL 1
25087#define MA_DSSCL_PRIORITY 2
25088#define MA_DSSCL_EXCLUSIVE 3
25089#define MA_DSSCL_WRITEPRIMARY 4
25090
25091#define MA_DSCAPS_PRIMARYMONO 0x00000001
25092#define MA_DSCAPS_PRIMARYSTEREO 0x00000002
25093#define MA_DSCAPS_PRIMARY8BIT 0x00000004
25094#define MA_DSCAPS_PRIMARY16BIT 0x00000008
25095#define MA_DSCAPS_CONTINUOUSRATE 0x00000010
25096#define MA_DSCAPS_EMULDRIVER 0x00000020
25097#define MA_DSCAPS_CERTIFIED 0x00000040
25098#define MA_DSCAPS_SECONDARYMONO 0x00000100
25099#define MA_DSCAPS_SECONDARYSTEREO 0x00000200
25100#define MA_DSCAPS_SECONDARY8BIT 0x00000400
25101#define MA_DSCAPS_SECONDARY16BIT 0x00000800
25102
25103#define MA_DSBCAPS_PRIMARYBUFFER 0x00000001
25104#define MA_DSBCAPS_STATIC 0x00000002
25105#define MA_DSBCAPS_LOCHARDWARE 0x00000004
25106#define MA_DSBCAPS_LOCSOFTWARE 0x00000008
25107#define MA_DSBCAPS_CTRL3D 0x00000010
25108#define MA_DSBCAPS_CTRLFREQUENCY 0x00000020
25109#define MA_DSBCAPS_CTRLPAN 0x00000040
25110#define MA_DSBCAPS_CTRLVOLUME 0x00000080
25111#define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100
25112#define MA_DSBCAPS_CTRLFX 0x00000200
25113#define MA_DSBCAPS_STICKYFOCUS 0x00004000
25114#define MA_DSBCAPS_GLOBALFOCUS 0x00008000
25115#define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000
25116#define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000
25117#define MA_DSBCAPS_LOCDEFER 0x00040000
25118#define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000
25119
25120#define MA_DSBPLAY_LOOPING 0x00000001
25121#define MA_DSBPLAY_LOCHARDWARE 0x00000002
25122#define MA_DSBPLAY_LOCSOFTWARE 0x00000004
25123#define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008
25124#define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010
25125#define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020
25126
25127#define MA_DSBSTATUS_PLAYING 0x00000001
25128#define MA_DSBSTATUS_BUFFERLOST 0x00000002
25129#define MA_DSBSTATUS_LOOPING 0x00000004
25130#define MA_DSBSTATUS_LOCHARDWARE 0x00000008
25131#define MA_DSBSTATUS_LOCSOFTWARE 0x00000010
25132#define MA_DSBSTATUS_TERMINATED 0x00000020
25133
25134#define MA_DSCBSTART_LOOPING 0x00000001
25135
25136typedef struct
25137{
25138 DWORD dwSize;
25139 DWORD dwFlags;
25140 DWORD dwBufferBytes;
25141 DWORD dwReserved;
25142 MA_WAVEFORMATEX* lpwfxFormat;
25143 GUID guid3DAlgorithm;
25144} MA_DSBUFFERDESC;
25145
25146typedef struct
25147{
25148 DWORD dwSize;
25149 DWORD dwFlags;
25150 DWORD dwBufferBytes;
25151 DWORD dwReserved;
25152 MA_WAVEFORMATEX* lpwfxFormat;
25153 DWORD dwFXCount;
25154 void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */
25155} MA_DSCBUFFERDESC;
25156
25157typedef struct
25158{
25159 DWORD dwSize;
25160 DWORD dwFlags;
25161 DWORD dwMinSecondarySampleRate;
25162 DWORD dwMaxSecondarySampleRate;
25163 DWORD dwPrimaryBuffers;
25164 DWORD dwMaxHwMixingAllBuffers;
25165 DWORD dwMaxHwMixingStaticBuffers;
25166 DWORD dwMaxHwMixingStreamingBuffers;
25167 DWORD dwFreeHwMixingAllBuffers;
25168 DWORD dwFreeHwMixingStaticBuffers;
25169 DWORD dwFreeHwMixingStreamingBuffers;
25170 DWORD dwMaxHw3DAllBuffers;
25171 DWORD dwMaxHw3DStaticBuffers;
25172 DWORD dwMaxHw3DStreamingBuffers;
25173 DWORD dwFreeHw3DAllBuffers;
25174 DWORD dwFreeHw3DStaticBuffers;
25175 DWORD dwFreeHw3DStreamingBuffers;
25176 DWORD dwTotalHwMemBytes;
25177 DWORD dwFreeHwMemBytes;
25178 DWORD dwMaxContigFreeHwMemBytes;
25179 DWORD dwUnlockTransferRateHwBuffers;
25180 DWORD dwPlayCpuOverheadSwBuffers;
25181 DWORD dwReserved1;
25182 DWORD dwReserved2;
25183} MA_DSCAPS;
25184
25185typedef struct
25186{
25187 DWORD dwSize;
25188 DWORD dwFlags;
25189 DWORD dwBufferBytes;
25190 DWORD dwUnlockTransferRate;
25191 DWORD dwPlayCpuOverhead;
25192} MA_DSBCAPS;
25193
25194typedef struct
25195{
25196 DWORD dwSize;
25197 DWORD dwFlags;
25198 DWORD dwFormats;
25199 DWORD dwChannels;
25200} MA_DSCCAPS;
25201
25202typedef struct
25203{
25204 DWORD dwSize;
25205 DWORD dwFlags;
25206 DWORD dwBufferBytes;
25207 DWORD dwReserved;
25208} MA_DSCBCAPS;
25209
25210typedef struct
25211{
25212 DWORD dwOffset;
25213 HANDLE hEventNotify;
25214} MA_DSBPOSITIONNOTIFY;
25215
25216typedef struct ma_IDirectSound ma_IDirectSound;
25217typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer;
25218typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture;
25219typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer;
25220typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify;
25221
25222
25223/*
25224COM objects. The way these work is that you have a vtable (a list of function pointers, kind of
25225like how C++ works internally), and then you have a structure with a single member, which is a
25226pointer to the vtable. The vtable is where the methods of the object are defined. Methods need
25227to be in a specific order, and parent classes need to have their methods declared first.
25228*/
25229
25230/* IDirectSound */
25231typedef struct
25232{
25233 /* IUnknown */
25234 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject);
25235 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis);
25236 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis);
25237
25238 /* IDirectSound */
25239 HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter);
25240 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps);
25241 HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate);
25242 HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel);
25243 HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis);
25244 HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig);
25245 HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig);
25246 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice);
25247} ma_IDirectSoundVtbl;
25248struct ma_IDirectSound
25249{
25250 ma_IDirectSoundVtbl* lpVtbl;
25251};
25252static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
25253static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); }
25254static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); }
25255static 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); }
25256static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); }
25257static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); }
25258static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); }
25259static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); }
25260static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); }
25261static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); }
25262static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
25263
25264
25265/* IDirectSoundBuffer */
25266typedef struct
25267{
25268 /* IUnknown */
25269 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject);
25270 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis);
25271 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis);
25272
25273 /* IDirectSoundBuffer */
25274 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps);
25275 HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor);
25276 HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
25277 HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume);
25278 HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan);
25279 HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency);
25280 HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus);
25281 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc);
25282 HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
25283 HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags);
25284 HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition);
25285 HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat);
25286 HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume);
25287 HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan);
25288 HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency);
25289 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis);
25290 HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
25291 HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis);
25292} ma_IDirectSoundBufferVtbl;
25293struct ma_IDirectSoundBuffer
25294{
25295 ma_IDirectSoundBufferVtbl* lpVtbl;
25296};
25297static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
25298static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
25299static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
25300static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); }
25301static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); }
25302static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
25303static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); }
25304static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); }
25305static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); }
25306static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
25307static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); }
25308static 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); }
25309static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); }
25310static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); }
25311static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); }
25312static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); }
25313static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); }
25314static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); }
25315static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
25316static 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); }
25317static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); }
25318
25319
25320/* IDirectSoundCapture */
25321typedef struct
25322{
25323 /* IUnknown */
25324 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject);
25325 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis);
25326 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis);
25327
25328 /* IDirectSoundCapture */
25329 HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter);
25330 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps);
25331 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice);
25332} ma_IDirectSoundCaptureVtbl;
25333struct ma_IDirectSoundCapture
25334{
25335 ma_IDirectSoundCaptureVtbl* lpVtbl;
25336};
25337static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
25338static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); }
25339static MA_INLINE ULONG ma_IDirectSoundCapture_Release (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); }
25340static 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); }
25341static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); }
25342static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
25343
25344
25345/* IDirectSoundCaptureBuffer */
25346typedef struct
25347{
25348 /* IUnknown */
25349 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject);
25350 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis);
25351 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis);
25352
25353 /* IDirectSoundCaptureBuffer */
25354 HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps);
25355 HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition);
25356 HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
25357 HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus);
25358 HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc);
25359 HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
25360 HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags);
25361 HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis);
25362 HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
25363} ma_IDirectSoundCaptureBufferVtbl;
25364struct ma_IDirectSoundCaptureBuffer
25365{
25366 ma_IDirectSoundCaptureBufferVtbl* lpVtbl;
25367};
25368static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
25369static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
25370static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
25371static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); }
25372static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); }
25373static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
25374static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
25375static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); }
25376static 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); }
25377static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); }
25378static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
25379static 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); }
25380
25381
25382/* IDirectSoundNotify */
25383typedef struct
25384{
25385 /* IUnknown */
25386 HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject);
25387 ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis);
25388 ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis);
25389
25390 /* IDirectSoundNotify */
25391 HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies);
25392} ma_IDirectSoundNotifyVtbl;
25393struct ma_IDirectSoundNotify
25394{
25395 ma_IDirectSoundNotifyVtbl* lpVtbl;
25396};
25397static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
25398static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); }
25399static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); }
25400static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); }
25401
25402
25403typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (GUID* pDeviceGUID, const char* pDeviceDescription, const char* pModule, void* pContext);
25404typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, ma_IUnknown* pUnkOuter);
25405typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext);
25406typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, ma_IUnknown* pUnkOuter);
25407typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext);
25408
25409static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax)
25410{
25411 /* Normalize the range in case we were given something stupid. */
25412 if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) {
25413 sampleRateMin = (ma_uint32)ma_standard_sample_rate_min;
25414 }
25415 if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) {
25416 sampleRateMax = (ma_uint32)ma_standard_sample_rate_max;
25417 }
25418 if (sampleRateMin > sampleRateMax) {
25419 sampleRateMin = sampleRateMax;
25420 }
25421
25422 if (sampleRateMin == sampleRateMax) {
25423 return sampleRateMax;
25424 } else {
25425 size_t iStandardRate;
25426 for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
25427 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
25428 if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) {
25429 return standardRate;
25430 }
25431 }
25432 }
25433
25434 /* Should never get here. */
25435 MA_ASSERT(MA_FALSE);
25436 return 0;
25437}
25438
25439/*
25440Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown,
25441the channel count and channel map will be left unmodified.
25442*/
25443static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut)
25444{
25445 WORD channels;
25446 DWORD channelMap;
25447
25448 channels = 0;
25449 if (pChannelsOut != NULL) {
25450 channels = *pChannelsOut;
25451 }
25452
25453 channelMap = 0;
25454 if (pChannelMapOut != NULL) {
25455 channelMap = *pChannelMapOut;
25456 }
25457
25458 /*
25459 The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper
25460 16 bits is for the geometry.
25461 */
25462 switch ((BYTE)(speakerConfig)) {
25463 case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
25464 case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break;
25465 case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
25466 case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
25467 case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break;
25468 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;
25469 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;
25470 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;
25471 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;
25472 default: break;
25473 }
25474
25475 if (pChannelsOut != NULL) {
25476 *pChannelsOut = channels;
25477 }
25478
25479 if (pChannelMapOut != NULL) {
25480 *pChannelMapOut = channelMap;
25481 }
25482}
25483
25484
25485static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound)
25486{
25487 ma_IDirectSound* pDirectSound;
25488 HWND hWnd;
25489 HRESULT hr;
25490
25491 MA_ASSERT(pContext != NULL);
25492 MA_ASSERT(ppDirectSound != NULL);
25493
25494 *ppDirectSound = NULL;
25495 pDirectSound = NULL;
25496
25497 if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) {
25498 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device.");
25500 }
25501
25502 /* The cooperative level must be set before doing anything else. */
25503 hWnd = (HWND)pContext->dsound.hWnd;
25504 if (hWnd == 0) {
25505 hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
25506 if (hWnd == 0) {
25507 hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
25508 }
25509 }
25510
25511 hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY);
25512 if (FAILED(hr)) {
25513 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.");
25514 return ma_result_from_HRESULT(hr);
25515 }
25516
25517 *ppDirectSound = pDirectSound;
25518 return MA_SUCCESS;
25519}
25520
25521static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture)
25522{
25523 ma_IDirectSoundCapture* pDirectSoundCapture;
25524 HRESULT hr;
25525
25526 MA_ASSERT(pContext != NULL);
25527 MA_ASSERT(ppDirectSoundCapture != NULL);
25528
25529 /* DirectSound does not support exclusive mode for capture. */
25530 if (shareMode == ma_share_mode_exclusive) {
25532 }
25533
25534 *ppDirectSoundCapture = NULL;
25535 pDirectSoundCapture = NULL;
25536
25537 hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL);
25538 if (FAILED(hr)) {
25539 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.");
25540 return ma_result_from_HRESULT(hr);
25541 }
25542
25543 *ppDirectSoundCapture = pDirectSoundCapture;
25544 return MA_SUCCESS;
25545}
25546
25547static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate)
25548{
25549 HRESULT hr;
25550 MA_DSCCAPS caps;
25551 WORD bitsPerSample;
25552 DWORD sampleRate;
25553
25554 MA_ASSERT(pContext != NULL);
25555 MA_ASSERT(pDirectSoundCapture != NULL);
25556
25557 if (pChannels) {
25558 *pChannels = 0;
25559 }
25560 if (pBitsPerSample) {
25561 *pBitsPerSample = 0;
25562 }
25563 if (pSampleRate) {
25564 *pSampleRate = 0;
25565 }
25566
25567 MA_ZERO_OBJECT(&caps);
25568 caps.dwSize = sizeof(caps);
25569 hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps);
25570 if (FAILED(hr)) {
25571 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device.");
25572 return ma_result_from_HRESULT(hr);
25573 }
25574
25575 if (pChannels) {
25576 *pChannels = (WORD)caps.dwChannels;
25577 }
25578
25579 /* 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. */
25580 bitsPerSample = 16;
25581 sampleRate = 48000;
25582
25583 if (caps.dwChannels == 1) {
25584 if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) {
25585 sampleRate = 48000;
25586 } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) {
25587 sampleRate = 44100;
25588 } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) {
25589 sampleRate = 22050;
25590 } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) {
25591 sampleRate = 11025;
25592 } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) {
25593 sampleRate = 96000;
25594 } else {
25595 bitsPerSample = 8;
25596 if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) {
25597 sampleRate = 48000;
25598 } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) {
25599 sampleRate = 44100;
25600 } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) {
25601 sampleRate = 22050;
25602 } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) {
25603 sampleRate = 11025;
25604 } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) {
25605 sampleRate = 96000;
25606 } else {
25607 bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
25608 }
25609 }
25610 } else if (caps.dwChannels == 2) {
25611 if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) {
25612 sampleRate = 48000;
25613 } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) {
25614 sampleRate = 44100;
25615 } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) {
25616 sampleRate = 22050;
25617 } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) {
25618 sampleRate = 11025;
25619 } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) {
25620 sampleRate = 96000;
25621 } else {
25622 bitsPerSample = 8;
25623 if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) {
25624 sampleRate = 48000;
25625 } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) {
25626 sampleRate = 44100;
25627 } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) {
25628 sampleRate = 22050;
25629 } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) {
25630 sampleRate = 11025;
25631 } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) {
25632 sampleRate = 96000;
25633 } else {
25634 bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
25635 }
25636 }
25637 }
25638
25639 if (pBitsPerSample) {
25640 *pBitsPerSample = bitsPerSample;
25641 }
25642 if (pSampleRate) {
25643 *pSampleRate = sampleRate;
25644 }
25645
25646 return MA_SUCCESS;
25647}
25648
25649
25650typedef struct
25651{
25652 ma_context* pContext;
25653 ma_device_type deviceType;
25655 void* pUserData;
25656 ma_bool32 terminated;
25657} ma_context_enumerate_devices_callback_data__dsound;
25658
25659static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext)
25660{
25661 ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext;
25662 ma_device_info deviceInfo;
25663
25664 (void)lpcstrModule;
25665
25666 MA_ZERO_OBJECT(&deviceInfo);
25667
25668 /* ID. */
25669 if (lpGuid != NULL) {
25670 MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16);
25671 } else {
25672 MA_ZERO_MEMORY(deviceInfo.id.dsound, 16);
25673 deviceInfo.isDefault = MA_TRUE;
25674 }
25675
25676 /* Name / Description */
25677 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1);
25678
25679
25680 /* Call the callback function, but make sure we stop enumerating if the callee requested so. */
25681 MA_ASSERT(pData != NULL);
25682 pData->terminated = (pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData) == MA_FALSE);
25683 if (pData->terminated) {
25684 return FALSE; /* Stop enumeration. */
25685 } else {
25686 return TRUE; /* Continue enumeration. */
25687 }
25688}
25689
25690static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
25691{
25692 ma_context_enumerate_devices_callback_data__dsound data;
25693
25694 MA_ASSERT(pContext != NULL);
25695 MA_ASSERT(callback != NULL);
25696
25697 data.pContext = pContext;
25698 data.callback = callback;
25699 data.pUserData = pUserData;
25700 data.terminated = MA_FALSE;
25701
25702 /* Playback. */
25703 if (!data.terminated) {
25704 data.deviceType = ma_device_type_playback;
25705 ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
25706 }
25707
25708 /* Capture. */
25709 if (!data.terminated) {
25710 data.deviceType = ma_device_type_capture;
25711 ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
25712 }
25713
25714 return MA_SUCCESS;
25715}
25716
25717
25718typedef struct
25719{
25720 const ma_device_id* pDeviceID;
25721 ma_device_info* pDeviceInfo;
25722 ma_bool32 found;
25723} ma_context_get_device_info_callback_data__dsound;
25724
25725static BOOL CALLBACK ma_context_get_device_info_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext)
25726{
25727 ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext;
25728 MA_ASSERT(pData != NULL);
25729
25730 if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) {
25731 /* Default device. */
25732 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
25733 pData->pDeviceInfo->isDefault = MA_TRUE;
25734 pData->found = MA_TRUE;
25735 return FALSE; /* Stop enumeration. */
25736 } else {
25737 /* Not the default device. */
25738 if (lpGuid != NULL && pData->pDeviceID != NULL) {
25739 if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) {
25740 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
25741 pData->found = MA_TRUE;
25742 return FALSE; /* Stop enumeration. */
25743 }
25744 }
25745 }
25746
25747 (void)lpcstrModule;
25748 return TRUE;
25749}
25750
25751static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
25752{
25753 ma_result result;
25754 HRESULT hr;
25755
25756 if (pDeviceID != NULL) {
25757 ma_context_get_device_info_callback_data__dsound data;
25758
25759 /* ID. */
25760 MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16);
25761
25762 /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */
25763 data.pDeviceID = pDeviceID;
25764 data.pDeviceInfo = pDeviceInfo;
25765 data.found = MA_FALSE;
25766 if (deviceType == ma_device_type_playback) {
25767 ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
25768 } else {
25769 ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
25770 }
25771
25772 if (!data.found) {
25773 return MA_NO_DEVICE;
25774 }
25775 } else {
25776 /* 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. */
25777
25778 /* ID */
25779 MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16);
25780
25781 /* Name / Description */
25782 if (deviceType == ma_device_type_playback) {
25783 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
25784 } else {
25785 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
25786 }
25787
25788 pDeviceInfo->isDefault = MA_TRUE;
25789 }
25790
25791 /* Retrieving detailed information is slightly different depending on the device type. */
25792 if (deviceType == ma_device_type_playback) {
25793 /* Playback. */
25794 ma_IDirectSound* pDirectSound;
25795 MA_DSCAPS caps;
25796 WORD channels;
25797
25798 result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound);
25799 if (result != MA_SUCCESS) {
25800 return result;
25801 }
25802
25803 MA_ZERO_OBJECT(&caps);
25804 caps.dwSize = sizeof(caps);
25805 hr = ma_IDirectSound_GetCaps(pDirectSound, &caps);
25806 if (FAILED(hr)) {
25807 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.");
25808 return ma_result_from_HRESULT(hr);
25809 }
25810
25811
25812 /* Channels. Only a single channel count is reported for DirectSound. */
25813 if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
25814 /* It supports at least stereo, but could support more. */
25815 DWORD speakerConfig;
25816
25817 channels = 2;
25818
25819 /* Look at the speaker configuration to get a better idea on the channel count. */
25820 hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig);
25821 if (SUCCEEDED(hr)) {
25822 ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL);
25823 }
25824 } else {
25825 /* It does not support stereo, which means we are stuck with mono. */
25826 channels = 1;
25827 }
25828
25829
25830 /*
25831 In DirectSound, our native formats are centered around sample rates. All formats are supported, and we're only reporting a single channel
25832 count. However, DirectSound can report a range of supported sample rates. We're only going to include standard rates known by miniaudio
25833 in order to keep the size of this within reason.
25834 */
25835 if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
25836 /* Multiple sample rates are supported. We'll report in order of our preferred sample rates. */
25837 size_t iStandardSampleRate;
25838 for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {
25839 ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];
25840 if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) {
25842 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
25843 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
25844 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
25845 pDeviceInfo->nativeDataFormatCount += 1;
25846 }
25847 }
25848 } else {
25849 /* Only a single sample rate is supported. */
25851 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
25852 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate;
25853 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
25854 pDeviceInfo->nativeDataFormatCount += 1;
25855 }
25856
25857 ma_IDirectSound_Release(pDirectSound);
25858 } else {
25859 /*
25860 Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture
25861 devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just
25862 reporting the best format.
25863 */
25864 ma_IDirectSoundCapture* pDirectSoundCapture;
25865 WORD channels;
25866 WORD bitsPerSample;
25867 DWORD sampleRate;
25868
25869 result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture);
25870 if (result != MA_SUCCESS) {
25871 return result;
25872 }
25873
25874 result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate);
25875 if (result != MA_SUCCESS) {
25876 ma_IDirectSoundCapture_Release(pDirectSoundCapture);
25877 return result;
25878 }
25879
25880 ma_IDirectSoundCapture_Release(pDirectSoundCapture);
25881
25882 /* The format is always an integer format and is based on the bits per sample. */
25883 if (bitsPerSample == 8) {
25884 pDeviceInfo->nativeDataFormats[0].format = ma_format_u8;
25885 } else if (bitsPerSample == 16) {
25886 pDeviceInfo->nativeDataFormats[0].format = ma_format_s16;
25887 } else if (bitsPerSample == 24) {
25888 pDeviceInfo->nativeDataFormats[0].format = ma_format_s24;
25889 } else if (bitsPerSample == 32) {
25890 pDeviceInfo->nativeDataFormats[0].format = ma_format_s32;
25891 } else {
25893 }
25894
25895 pDeviceInfo->nativeDataFormats[0].channels = channels;
25896 pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate;
25897 pDeviceInfo->nativeDataFormats[0].flags = 0;
25898 pDeviceInfo->nativeDataFormatCount = 1;
25899 }
25900
25901 return MA_SUCCESS;
25902}
25903
25904
25905
25906static ma_result ma_device_uninit__dsound(ma_device* pDevice)
25907{
25908 MA_ASSERT(pDevice != NULL);
25909
25910 if (pDevice->dsound.pCaptureBuffer != NULL) {
25911 ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
25912 }
25913 if (pDevice->dsound.pCapture != NULL) {
25914 ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture);
25915 }
25916
25917 if (pDevice->dsound.pPlaybackBuffer != NULL) {
25918 ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
25919 }
25920 if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) {
25921 ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer);
25922 }
25923 if (pDevice->dsound.pPlayback != NULL) {
25924 ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback);
25925 }
25926
25927 return MA_SUCCESS;
25928}
25929
25930static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, MA_WAVEFORMATEXTENSIBLE* pWF)
25931{
25932 GUID subformat;
25933
25934 if (format == ma_format_unknown) {
25935 format = MA_DEFAULT_FORMAT;
25936 }
25937
25938 if (channels == 0) {
25939 channels = MA_DEFAULT_CHANNELS;
25940 }
25941
25942 if (sampleRate == 0) {
25943 sampleRate = MA_DEFAULT_SAMPLE_RATE;
25944 }
25945
25946 switch (format)
25947 {
25948 case ma_format_u8:
25949 case ma_format_s16:
25950 case ma_format_s24:
25951 /*case ma_format_s24_32:*/
25952 case ma_format_s32:
25953 {
25954 subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
25955 } break;
25956
25957 case ma_format_f32:
25958 {
25959 subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
25960 } break;
25961
25962 default:
25964 }
25965
25966 MA_ZERO_OBJECT(pWF);
25967 pWF->cbSize = sizeof(*pWF);
25968 pWF->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
25969 pWF->nChannels = (WORD)channels;
25970 pWF->nSamplesPerSec = (DWORD)sampleRate;
25971 pWF->wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8);
25972 pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8);
25973 pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec;
25974 pWF->Samples.wValidBitsPerSample = pWF->wBitsPerSample;
25975 pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels);
25976 pWF->SubFormat = subformat;
25977
25978 return MA_SUCCESS;
25979}
25980
25981static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
25982{
25983 /*
25984 DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for
25985 reliable glitch-free processing so going to use 30ms instead.
25986 */
25987 ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate);
25988 ma_uint32 periodSizeInFrames;
25989
25990 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);
25991 if (periodSizeInFrames < minPeriodSizeInFrames) {
25992 periodSizeInFrames = minPeriodSizeInFrames;
25993 }
25994
25995 return periodSizeInFrames;
25996}
25997
25998static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
25999{
26000 ma_result result;
26001 HRESULT hr;
26002
26003 MA_ASSERT(pDevice != NULL);
26004
26005 MA_ZERO_OBJECT(&pDevice->dsound);
26006
26007 if (pConfig->deviceType == ma_device_type_loopback) {
26009 }
26010
26011 /*
26012 Unfortunately DirectSound uses different APIs and data structures for playback and capture devices. We need to initialize
26013 the capture device first because we'll want to match its buffer size and period count on the playback side if we're using
26014 full-duplex mode.
26015 */
26016 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
26017 MA_WAVEFORMATEXTENSIBLE wf;
26018 MA_DSCBUFFERDESC descDS;
26019 ma_uint32 periodSizeInFrames;
26020 ma_uint32 periodCount;
26021 char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
26022 MA_WAVEFORMATEXTENSIBLE* pActualFormat;
26023
26024 result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf);
26025 if (result != MA_SUCCESS) {
26026 return result;
26027 }
26028
26029 result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture);
26030 if (result != MA_SUCCESS) {
26031 ma_device_uninit__dsound(pDevice);
26032 return result;
26033 }
26034
26035 result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.nChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec);
26036 if (result != MA_SUCCESS) {
26037 ma_device_uninit__dsound(pDevice);
26038 return result;
26039 }
26040
26041 wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8);
26042 wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;
26043 wf.Samples.wValidBitsPerSample = wf.wBitsPerSample;
26044 wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
26045
26046 /* The size of the buffer must be a clean multiple of the period count. */
26047 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.nSamplesPerSec, pConfig->performanceProfile);
26048 periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS;
26049
26050 MA_ZERO_OBJECT(&descDS);
26051 descDS.dwSize = sizeof(descDS);
26052 descDS.dwFlags = 0;
26053 descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.nBlockAlign;
26054 descDS.lpwfxFormat = (MA_WAVEFORMATEX*)&wf;
26055 hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);
26056 if (FAILED(hr)) {
26057 ma_device_uninit__dsound(pDevice);
26058 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.");
26059 return ma_result_from_HRESULT(hr);
26060 }
26061
26062 /* Get the _actual_ properties of the buffer. */
26063 pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata;
26064 hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);
26065 if (FAILED(hr)) {
26066 ma_device_uninit__dsound(pDevice);
26067 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.");
26068 return ma_result_from_HRESULT(hr);
26069 }
26070
26071 /* We can now start setting the output data formats. */
26072 pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat);
26073 pDescriptorCapture->channels = pActualFormat->nChannels;
26074 pDescriptorCapture->sampleRate = pActualFormat->nSamplesPerSec;
26075
26076 /* Get the native channel map based on the channel mask. */
26077 if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
26078 ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
26079 } else {
26080 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
26081 }
26082
26083 /*
26084 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
26085 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.
26086 */
26087 if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) {
26088 descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount;
26089 ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
26090
26091 hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);
26092 if (FAILED(hr)) {
26093 ma_device_uninit__dsound(pDevice);
26094 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.");
26095 return ma_result_from_HRESULT(hr);
26096 }
26097 }
26098
26099 /* DirectSound should give us a buffer exactly the size we asked for. */
26100 pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
26101 pDescriptorCapture->periodCount = periodCount;
26102 }
26103
26104 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
26105 MA_WAVEFORMATEXTENSIBLE wf;
26106 MA_DSBUFFERDESC descDSPrimary;
26107 MA_DSCAPS caps;
26108 char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
26109 MA_WAVEFORMATEXTENSIBLE* pActualFormat;
26110 ma_uint32 periodSizeInFrames;
26111 ma_uint32 periodCount;
26112 MA_DSBUFFERDESC descDS;
26113 WORD nativeChannelCount;
26114 DWORD nativeChannelMask = 0;
26115
26116 result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf);
26117 if (result != MA_SUCCESS) {
26118 return result;
26119 }
26120
26121 result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback);
26122 if (result != MA_SUCCESS) {
26123 ma_device_uninit__dsound(pDevice);
26124 return result;
26125 }
26126
26127 MA_ZERO_OBJECT(&descDSPrimary);
26128 descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC);
26129 descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME;
26130 hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL);
26131 if (FAILED(hr)) {
26132 ma_device_uninit__dsound(pDevice);
26133 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.");
26134 return ma_result_from_HRESULT(hr);
26135 }
26136
26137
26138 /* We may want to make some adjustments to the format if we are using defaults. */
26139 MA_ZERO_OBJECT(&caps);
26140 caps.dwSize = sizeof(caps);
26141 hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps);
26142 if (FAILED(hr)) {
26143 ma_device_uninit__dsound(pDevice);
26144 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.");
26145 return ma_result_from_HRESULT(hr);
26146 }
26147
26148 if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
26149 DWORD speakerConfig;
26150
26151 /* It supports at least stereo, but could support more. */
26152 nativeChannelCount = 2;
26153
26154 /* Look at the speaker configuration to get a better idea on the channel count. */
26155 if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) {
26156 ma_get_channels_from_speaker_config__dsound(speakerConfig, &nativeChannelCount, &nativeChannelMask);
26157 }
26158 } else {
26159 /* It does not support stereo, which means we are stuck with mono. */
26160 nativeChannelCount = 1;
26161 nativeChannelMask = 0x00000001;
26162 }
26163
26164 if (pDescriptorPlayback->channels == 0) {
26165 wf.nChannels = nativeChannelCount;
26166 wf.dwChannelMask = nativeChannelMask;
26167 }
26168
26169 if (pDescriptorPlayback->sampleRate == 0) {
26170 /* We base the sample rate on the values returned by GetCaps(). */
26171 if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
26172 wf.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate);
26173 } else {
26174 wf.nSamplesPerSec = caps.dwMaxSecondarySampleRate;
26175 }
26176 }
26177
26178 wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8);
26179 wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;
26180
26181 /*
26182 From MSDN:
26183
26184 The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest
26185 supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer
26186 and compare the result with the format that was requested with the SetFormat method.
26187 */
26188 hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf);
26189 if (FAILED(hr)) {
26190 /*
26191 If setting of the format failed we'll try again with some fallback settings. On Windows 98 I have
26192 observed that IEEE_FLOAT does not work. We'll therefore enforce PCM. I also had issues where a
26193 sample rate of 48000 did not work correctly. Not sure if it was a driver issue or not, but will
26194 use 44100 for the sample rate.
26195 */
26196 wf.cbSize = 18; /* NOTE: Don't use sizeof(MA_WAVEFORMATEX) here because it's got an extra 2 bytes due to padding. */
26197 wf.wFormatTag = WAVE_FORMAT_PCM;
26198 wf.wBitsPerSample = 16;
26199 wf.nChannels = nativeChannelCount;
26200 wf.nSamplesPerSec = 44100;
26201 wf.nBlockAlign = wf.nChannels * (wf.wBitsPerSample / 8);
26202 wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
26203
26204 hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf);
26205 if (FAILED(hr)) {
26206 ma_device_uninit__dsound(pDevice);
26207 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer.");
26208 return ma_result_from_HRESULT(hr);
26209 }
26210 }
26211
26212 /* Get the _actual_ properties of the buffer. */
26213 pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata;
26214 hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);
26215 if (FAILED(hr)) {
26216 ma_device_uninit__dsound(pDevice);
26217 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.");
26218 return ma_result_from_HRESULT(hr);
26219 }
26220
26221 /* We now have enough information to start setting some output properties. */
26222 pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat);
26223 pDescriptorPlayback->channels = pActualFormat->nChannels;
26224 pDescriptorPlayback->sampleRate = pActualFormat->nSamplesPerSec;
26225
26226 /* Get the internal channel map based on the channel mask. */
26227 if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
26228 ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
26229 } else {
26230 ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
26231 }
26232
26233 /* The size of the buffer must be a clean multiple of the period count. */
26234 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
26235 periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS;
26236
26237 /*
26238 Meaning of dwFlags (from MSDN):
26239
26240 DSBCAPS_CTRLPOSITIONNOTIFY
26241 The buffer has position notification capability.
26242
26243 DSBCAPS_GLOBALFOCUS
26244 With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to
26245 another application, even if the new application uses DirectSound.
26246
26247 DSBCAPS_GETCURRENTPOSITION2
26248 In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated
26249 sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the
26250 application can get a more accurate play cursor.
26251 */
26252 MA_ZERO_OBJECT(&descDS);
26253 descDS.dwSize = sizeof(descDS);
26254 descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2;
26255 descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels);
26256 descDS.lpwfxFormat = (MA_WAVEFORMATEX*)pActualFormat;
26257 hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL);
26258 if (FAILED(hr)) {
26259 ma_device_uninit__dsound(pDevice);
26260 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.");
26261 return ma_result_from_HRESULT(hr);
26262 }
26263
26264 /* DirectSound should give us a buffer exactly the size we asked for. */
26265 pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
26266 pDescriptorPlayback->periodCount = periodCount;
26267 }
26268
26269 return MA_SUCCESS;
26270}
26271
26272
26273static ma_result ma_device_data_loop__dsound(ma_device* pDevice)
26274{
26275 ma_result result = MA_SUCCESS;
26276 ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
26277 ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
26278 HRESULT hr;
26279 DWORD lockOffsetInBytesCapture;
26280 DWORD lockSizeInBytesCapture;
26281 DWORD mappedSizeInBytesCapture;
26282 DWORD mappedDeviceFramesProcessedCapture;
26283 void* pMappedDeviceBufferCapture;
26284 DWORD lockOffsetInBytesPlayback;
26285 DWORD lockSizeInBytesPlayback;
26286 DWORD mappedSizeInBytesPlayback;
26287 void* pMappedDeviceBufferPlayback;
26288 DWORD prevReadCursorInBytesCapture = 0;
26289 DWORD prevPlayCursorInBytesPlayback = 0;
26290 ma_bool32 physicalPlayCursorLoopFlagPlayback = 0;
26291 DWORD virtualWriteCursorInBytesPlayback = 0;
26292 ma_bool32 virtualWriteCursorLoopFlagPlayback = 0;
26293 ma_bool32 isPlaybackDeviceStarted = MA_FALSE;
26294 ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */
26295 ma_uint32 waitTimeInMilliseconds = 1;
26296 DWORD playbackBufferStatus = 0;
26297
26298 MA_ASSERT(pDevice != NULL);
26299
26300 /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */
26301 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26302 hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING);
26303 if (FAILED(hr)) {
26304 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed.");
26305 return ma_result_from_HRESULT(hr);
26306 }
26307 }
26308
26309 while (ma_device_get_state(pDevice) == ma_device_state_started) {
26310 switch (pDevice->type)
26311 {
26313 {
26314 DWORD physicalCaptureCursorInBytes;
26315 DWORD physicalReadCursorInBytes;
26316 hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
26317 if (FAILED(hr)) {
26318 return ma_result_from_HRESULT(hr);
26319 }
26320
26321 /* If nothing is available we just sleep for a bit and return from this iteration. */
26322 if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) {
26323 ma_sleep(waitTimeInMilliseconds);
26324 continue; /* Nothing is available in the capture buffer. */
26325 }
26326
26327 /*
26328 The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure
26329 we don't return until every frame has been copied over.
26330 */
26331 if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
26332 /* The capture position has not looped. This is the simple case. */
26333 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
26334 lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
26335 } else {
26336 /*
26337 The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
26338 do it again from the start.
26339 */
26340 if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
26341 /* Lock up to the end of the buffer. */
26342 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
26343 lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
26344 } else {
26345 /* Lock starting from the start of the buffer. */
26346 lockOffsetInBytesCapture = 0;
26347 lockSizeInBytesCapture = physicalReadCursorInBytes;
26348 }
26349 }
26350
26351 if (lockSizeInBytesCapture == 0) {
26352 ma_sleep(waitTimeInMilliseconds);
26353 continue; /* Nothing is available in the capture buffer. */
26354 }
26355
26356 hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
26357 if (FAILED(hr)) {
26358 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.");
26359 return ma_result_from_HRESULT(hr);
26360 }
26361
26362
26363 /* 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. */
26364 mappedDeviceFramesProcessedCapture = 0;
26365
26366 for (;;) { /* Keep writing to the playback device. */
26367 ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
26368 ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
26369 ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
26370 ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
26371 ma_uint32 outputFramesInClientFormatCount;
26372 ma_uint32 outputFramesInClientFormatConsumed = 0;
26373 ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap);
26374 ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture;
26375 void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture);
26376
26377 result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess);
26378 if (result != MA_SUCCESS) {
26379 break;
26380 }
26381
26382 outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess;
26383 mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess;
26384
26385 ma_device__handle_data_callback(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess);
26386
26387 /* 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. */
26388 for (;;) {
26389 ma_uint32 framesWrittenThisIteration;
26390 DWORD physicalPlayCursorInBytes;
26391 DWORD physicalWriteCursorInBytes;
26392 DWORD availableBytesPlayback;
26393 DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */
26394
26395 /* We need the physical play and write cursors. */
26396 if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
26397 break;
26398 }
26399
26400 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
26401 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
26402 }
26403 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
26404
26405 /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
26406 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
26407 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
26408 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
26409 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
26410 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
26411 } else {
26412 /* This is an error. */
26413 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);
26414 availableBytesPlayback = 0;
26415 }
26416 } else {
26417 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
26418 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
26419 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
26420 } else {
26421 /* This is an error. */
26422 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);
26423 availableBytesPlayback = 0;
26424 }
26425 }
26426
26427 /* If there's no room available for writing we need to wait for more. */
26428 if (availableBytesPlayback == 0) {
26429 /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
26430 if (!isPlaybackDeviceStarted) {
26431 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
26432 if (FAILED(hr)) {
26433 ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
26434 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
26435 return ma_result_from_HRESULT(hr);
26436 }
26437 isPlaybackDeviceStarted = MA_TRUE;
26438 } else {
26439 ma_sleep(waitTimeInMilliseconds);
26440 continue;
26441 }
26442 }
26443
26444
26445 /* 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. */
26446 lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
26447 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
26448 /* Same loop iteration. Go up to the end of the buffer. */
26449 lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
26450 } else {
26451 /* Different loop iterations. Go up to the physical play cursor. */
26452 lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
26453 }
26454
26455 hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
26456 if (FAILED(hr)) {
26457 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.");
26458 result = ma_result_from_HRESULT(hr);
26459 break;
26460 }
26461
26462 /*
26463 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
26464 endless glitching due to it constantly running out of data.
26465 */
26466 if (isPlaybackDeviceStarted) {
26467 DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback;
26468 if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) {
26469 silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback;
26470 if (silentPaddingInBytes > lockSizeInBytesPlayback) {
26471 silentPaddingInBytes = lockSizeInBytesPlayback;
26472 }
26473
26474 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);
26475 }
26476 }
26477
26478 /* At this point we have a buffer for output. */
26479 if (silentPaddingInBytes > 0) {
26480 MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes);
26481 framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback;
26482 } else {
26483 ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed);
26484 ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback;
26485 void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback);
26486 void* pConvertedFramesOut = pMappedDeviceBufferPlayback;
26487
26488 result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut);
26489 if (result != MA_SUCCESS) {
26490 break;
26491 }
26492
26493 outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut;
26494 framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut;
26495 }
26496
26497
26498 hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0);
26499 if (FAILED(hr)) {
26500 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.");
26501 result = ma_result_from_HRESULT(hr);
26502 break;
26503 }
26504
26505 virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback;
26506 if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) {
26507 virtualWriteCursorInBytesPlayback = 0;
26508 virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
26509 }
26510
26511 /*
26512 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
26513 a bit of a buffer to prevent the playback buffer from getting starved.
26514 */
26515 framesWrittenToPlaybackDevice += framesWrittenThisIteration;
26516 if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) {
26517 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
26518 if (FAILED(hr)) {
26519 ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
26520 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
26521 return ma_result_from_HRESULT(hr);
26522 }
26523 isPlaybackDeviceStarted = MA_TRUE;
26524 }
26525
26526 if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) {
26527 break; /* We're finished with the output data.*/
26528 }
26529 }
26530
26531 if (clientCapturedFramesToProcess == 0) {
26532 break; /* We just consumed every input sample. */
26533 }
26534 }
26535
26536
26537 /* At this point we're done with the mapped portion of the capture buffer. */
26538 hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
26539 if (FAILED(hr)) {
26540 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.");
26541 return ma_result_from_HRESULT(hr);
26542 }
26543 prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture);
26544 } break;
26545
26546
26547
26549 {
26550 DWORD physicalCaptureCursorInBytes;
26551 DWORD physicalReadCursorInBytes;
26552 hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
26553 if (FAILED(hr)) {
26554 return MA_ERROR;
26555 }
26556
26557 /* If the previous capture position is the same as the current position we need to wait a bit longer. */
26558 if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) {
26559 ma_sleep(waitTimeInMilliseconds);
26560 continue;
26561 }
26562
26563 /* Getting here means we have capture data available. */
26564 if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
26565 /* The capture position has not looped. This is the simple case. */
26566 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
26567 lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
26568 } else {
26569 /*
26570 The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
26571 do it again from the start.
26572 */
26573 if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
26574 /* Lock up to the end of the buffer. */
26575 lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
26576 lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
26577 } else {
26578 /* Lock starting from the start of the buffer. */
26579 lockOffsetInBytesCapture = 0;
26580 lockSizeInBytesCapture = physicalReadCursorInBytes;
26581 }
26582 }
26583
26584 if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) {
26585 ma_sleep(waitTimeInMilliseconds);
26586 continue; /* Nothing is available in the capture buffer. */
26587 }
26588
26589 hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
26590 if (FAILED(hr)) {
26591 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.");
26592 result = ma_result_from_HRESULT(hr);
26593 }
26594
26595 if (lockSizeInBytesCapture != mappedSizeInBytesCapture) {
26596 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture);
26597 }
26598
26599 ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture);
26600
26601 hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
26602 if (FAILED(hr)) {
26603 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.");
26604 return ma_result_from_HRESULT(hr);
26605 }
26606 prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture;
26607
26608 if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) {
26609 prevReadCursorInBytesCapture = 0;
26610 }
26611 } break;
26612
26613
26614
26616 {
26617 DWORD availableBytesPlayback;
26618 DWORD physicalPlayCursorInBytes;
26619 DWORD physicalWriteCursorInBytes;
26620 hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
26621 if (FAILED(hr)) {
26622 break;
26623 }
26624
26625 hr = ma_IDirectSoundBuffer_GetStatus((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &playbackBufferStatus);
26626 if (SUCCEEDED(hr) && (playbackBufferStatus & MA_DSBSTATUS_PLAYING) == 0 && isPlaybackDeviceStarted) {
26627 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[DirectSound] Attempting to resume audio due to state: %d.", (int)playbackBufferStatus);
26628 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
26629 if (FAILED(hr)) {
26630 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed after attempting to resume from state %d.", (int)playbackBufferStatus);
26631 return ma_result_from_HRESULT(hr);
26632 }
26633
26634 isPlaybackDeviceStarted = MA_TRUE;
26635 ma_sleep(waitTimeInMilliseconds);
26636 continue;
26637 }
26638
26639 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
26640 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
26641 }
26642 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
26643
26644 /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
26645 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
26646 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
26647 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
26648 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
26649 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
26650 } else {
26651 /* This is an error. */
26652 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);
26653 availableBytesPlayback = 0;
26654 }
26655 } else {
26656 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
26657 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
26658 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
26659 } else {
26660 /* This is an error. */
26661 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);
26662 availableBytesPlayback = 0;
26663 }
26664 }
26665
26666 /* If there's no room available for writing we need to wait for more. */
26667 if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) {
26668 /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
26669 if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) {
26670 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
26671 if (FAILED(hr)) {
26672 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
26673 return ma_result_from_HRESULT(hr);
26674 }
26675 isPlaybackDeviceStarted = MA_TRUE;
26676 } else {
26677 ma_sleep(waitTimeInMilliseconds);
26678 continue;
26679 }
26680 }
26681
26682 /* 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. */
26683 lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
26684 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
26685 /* Same loop iteration. Go up to the end of the buffer. */
26686 lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
26687 } else {
26688 /* Different loop iterations. Go up to the physical play cursor. */
26689 lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
26690 }
26691
26692 hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
26693 if (FAILED(hr)) {
26694 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.");
26695 result = ma_result_from_HRESULT(hr);
26696 break;
26697 }
26698
26699 /* At this point we have a buffer for output. */
26700 ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback);
26701
26702 hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0);
26703 if (FAILED(hr)) {
26704 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.");
26705 result = ma_result_from_HRESULT(hr);
26706 break;
26707 }
26708
26709 virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback;
26710 if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) {
26711 virtualWriteCursorInBytesPlayback = 0;
26712 virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
26713 }
26714
26715 /*
26716 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
26717 a bit of a buffer to prevent the playback buffer from getting starved.
26718 */
26719 framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback;
26720 if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) {
26721 hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
26722 if (FAILED(hr)) {
26723 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
26724 return ma_result_from_HRESULT(hr);
26725 }
26726 isPlaybackDeviceStarted = MA_TRUE;
26727 }
26728 } break;
26729
26730
26731 default: return MA_INVALID_ARGS; /* Invalid device type. */
26732 }
26733
26734 if (result != MA_SUCCESS) {
26735 return result;
26736 }
26737 }
26738
26739 /* Getting here means the device is being stopped. */
26740 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26741 hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
26742 if (FAILED(hr)) {
26743 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.");
26744 return ma_result_from_HRESULT(hr);
26745 }
26746 }
26747
26748 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26749 /* 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. */
26750 if (isPlaybackDeviceStarted) {
26751 for (;;) {
26752 DWORD availableBytesPlayback = 0;
26753 DWORD physicalPlayCursorInBytes;
26754 DWORD physicalWriteCursorInBytes;
26755 hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
26756 if (FAILED(hr)) {
26757 break;
26758 }
26759
26760 if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
26761 physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
26762 }
26763 prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
26764
26765 if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
26766 /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
26767 if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
26768 availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
26769 availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
26770 } else {
26771 break;
26772 }
26773 } else {
26774 /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
26775 if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
26776 availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
26777 } else {
26778 break;
26779 }
26780 }
26781
26782 if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) {
26783 break;
26784 }
26785
26786 ma_sleep(waitTimeInMilliseconds);
26787 }
26788 }
26789
26790 hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
26791 if (FAILED(hr)) {
26792 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed.");
26793 return ma_result_from_HRESULT(hr);
26794 }
26795
26796 ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0);
26797 }
26798
26799 return MA_SUCCESS;
26800}
26801
26802static ma_result ma_context_uninit__dsound(ma_context* pContext)
26803{
26804 MA_ASSERT(pContext != NULL);
26805 MA_ASSERT(pContext->backend == ma_backend_dsound);
26806
26807 ma_dlclose(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL);
26808
26809 return MA_SUCCESS;
26810}
26811
26812static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
26813{
26814 MA_ASSERT(pContext != NULL);
26815
26816 (void)pConfig;
26817
26818 pContext->dsound.hDSoundDLL = ma_dlopen(ma_context_get_log(pContext), "dsound.dll");
26819 if (pContext->dsound.hDSoundDLL == NULL) {
26820 return MA_API_NOT_FOUND;
26821 }
26822
26823 pContext->dsound.DirectSoundCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCreate");
26824 pContext->dsound.DirectSoundEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA");
26825 pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate");
26826 pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA");
26827
26828 /*
26829 We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too
26830 well in my testing. For example, it's missing DirectSoundCaptureEnumerateA(). This is a convenient
26831 place to just disable the DirectSound backend for Windows 95.
26832 */
26833 if (pContext->dsound.DirectSoundCreate == NULL ||
26834 pContext->dsound.DirectSoundEnumerateA == NULL ||
26835 pContext->dsound.DirectSoundCaptureCreate == NULL ||
26836 pContext->dsound.DirectSoundCaptureEnumerateA == NULL) {
26837 return MA_API_NOT_FOUND;
26838 }
26839
26840 pContext->dsound.hWnd = pConfig->dsound.hWnd;
26841
26842 pCallbacks->onContextInit = ma_context_init__dsound;
26843 pCallbacks->onContextUninit = ma_context_uninit__dsound;
26844 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound;
26845 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound;
26846 pCallbacks->onDeviceInit = ma_device_init__dsound;
26847 pCallbacks->onDeviceUninit = ma_device_uninit__dsound;
26848 pCallbacks->onDeviceStart = NULL; /* Not used. Started in onDeviceDataLoop. */
26849 pCallbacks->onDeviceStop = NULL; /* Not used. Stopped in onDeviceDataLoop. */
26850 pCallbacks->onDeviceRead = NULL; /* Not used. Data is read directly in onDeviceDataLoop. */
26851 pCallbacks->onDeviceWrite = NULL; /* Not used. Data is written directly in onDeviceDataLoop. */
26852 pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound;
26853
26854 return MA_SUCCESS;
26855}
26856#endif
26857
26858
26859
26860/******************************************************************************
26861
26862WinMM Backend
26863
26864******************************************************************************/
26865#ifdef MA_HAS_WINMM
26866
26867/*
26868Some build configurations will exclude the WinMM API. An example is when WIN32_LEAN_AND_MEAN
26869is defined. We need to define the types and functions we need manually.
26870*/
26871#define MA_MMSYSERR_NOERROR 0
26872#define MA_MMSYSERR_ERROR 1
26873#define MA_MMSYSERR_BADDEVICEID 2
26874#define MA_MMSYSERR_INVALHANDLE 5
26875#define MA_MMSYSERR_NOMEM 7
26876#define MA_MMSYSERR_INVALFLAG 10
26877#define MA_MMSYSERR_INVALPARAM 11
26878#define MA_MMSYSERR_HANDLEBUSY 12
26879
26880#define MA_CALLBACK_EVENT 0x00050000
26881#define MA_WAVE_ALLOWSYNC 0x0002
26882
26883#define MA_WHDR_DONE 0x00000001
26884#define MA_WHDR_PREPARED 0x00000002
26885#define MA_WHDR_BEGINLOOP 0x00000004
26886#define MA_WHDR_ENDLOOP 0x00000008
26887#define MA_WHDR_INQUEUE 0x00000010
26888
26889#define MA_MAXPNAMELEN 32
26890
26891typedef void* MA_HWAVEIN;
26892typedef void* MA_HWAVEOUT;
26893typedef UINT MA_MMRESULT;
26894typedef UINT MA_MMVERSION;
26895
26896typedef struct
26897{
26898 WORD wMid;
26899 WORD wPid;
26900 MA_MMVERSION vDriverVersion;
26901 CHAR szPname[MA_MAXPNAMELEN];
26902 DWORD dwFormats;
26903 WORD wChannels;
26904 WORD wReserved1;
26905} MA_WAVEINCAPSA;
26906
26907typedef struct
26908{
26909 WORD wMid;
26910 WORD wPid;
26911 MA_MMVERSION vDriverVersion;
26912 CHAR szPname[MA_MAXPNAMELEN];
26913 DWORD dwFormats;
26914 WORD wChannels;
26915 WORD wReserved1;
26916 DWORD dwSupport;
26917} MA_WAVEOUTCAPSA;
26918
26919typedef struct tagWAVEHDR
26920{
26921 char* lpData;
26922 DWORD dwBufferLength;
26923 DWORD dwBytesRecorded;
26924 DWORD_PTR dwUser;
26925 DWORD dwFlags;
26926 DWORD dwLoops;
26927 struct tagWAVEHDR* lpNext;
26928 DWORD_PTR reserved;
26929} MA_WAVEHDR;
26930
26931typedef struct
26932{
26933 WORD wMid;
26934 WORD wPid;
26935 MA_MMVERSION vDriverVersion;
26936 CHAR szPname[MA_MAXPNAMELEN];
26937 DWORD dwFormats;
26938 WORD wChannels;
26939 WORD wReserved1;
26940 DWORD dwSupport;
26941 GUID ManufacturerGuid;
26942 GUID ProductGuid;
26943 GUID NameGuid;
26944} MA_WAVEOUTCAPS2A;
26945
26946typedef struct
26947{
26948 WORD wMid;
26949 WORD wPid;
26950 MA_MMVERSION vDriverVersion;
26951 CHAR szPname[MA_MAXPNAMELEN];
26952 DWORD dwFormats;
26953 WORD wChannels;
26954 WORD wReserved1;
26955 GUID ManufacturerGuid;
26956 GUID ProductGuid;
26957 GUID NameGuid;
26958} MA_WAVEINCAPS2A;
26959
26960typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void);
26961typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEOUTCAPSA* pwoc, UINT cbwoc);
26962typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutOpen)(MA_HWAVEOUT* phwo, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
26963typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutClose)(MA_HWAVEOUT hwo);
26964typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh);
26965typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh);
26966typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutWrite)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh);
26967typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutReset)(MA_HWAVEOUT hwo);
26968typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void);
26969typedef MA_MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEINCAPSA* pwic, UINT cbwic);
26970typedef MA_MMRESULT (WINAPI * MA_PFN_waveInOpen)(MA_HWAVEIN* phwi, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
26971typedef MA_MMRESULT (WINAPI * MA_PFN_waveInClose)(MA_HWAVEIN hwi);
26972typedef MA_MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh);
26973typedef MA_MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh);
26974typedef MA_MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh);
26975typedef MA_MMRESULT (WINAPI * MA_PFN_waveInStart)(MA_HWAVEIN hwi);
26976typedef MA_MMRESULT (WINAPI * MA_PFN_waveInReset)(MA_HWAVEIN hwi);
26977
26978static ma_result ma_result_from_MMRESULT(MA_MMRESULT resultMM)
26979{
26980 switch (resultMM)
26981 {
26982 case MA_MMSYSERR_NOERROR: return MA_SUCCESS;
26983 case MA_MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS;
26984 case MA_MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS;
26985 case MA_MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY;
26986 case MA_MMSYSERR_INVALFLAG: return MA_INVALID_ARGS;
26987 case MA_MMSYSERR_INVALPARAM: return MA_INVALID_ARGS;
26988 case MA_MMSYSERR_HANDLEBUSY: return MA_BUSY;
26989 case MA_MMSYSERR_ERROR: return MA_ERROR;
26990 default: return MA_ERROR;
26991 }
26992}
26993
26994static char* ma_find_last_character(char* str, char ch)
26995{
26996 char* last;
26997
26998 if (str == NULL) {
26999 return NULL;
27000 }
27001
27002 last = NULL;
27003 while (*str != '\0') {
27004 if (*str == ch) {
27005 last = str;
27006 }
27007
27008 str += 1;
27009 }
27010
27011 return last;
27012}
27013
27014static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels)
27015{
27016 return periodSizeInFrames * ma_get_bytes_per_frame(format, channels);
27017}
27018
27019
27020/*
27021Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so
27022we can do things generically and typesafely. Names are being kept the same for consistency.
27023*/
27024typedef struct
27025{
27026 CHAR szPname[MA_MAXPNAMELEN];
27027 DWORD dwFormats;
27028 WORD wChannels;
27029 GUID NameGuid;
27030} MA_WAVECAPSA;
27031
27032static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate)
27033{
27034 WORD bitsPerSample = 0;
27035 DWORD sampleRate = 0;
27036
27037 if (pBitsPerSample) {
27038 *pBitsPerSample = 0;
27039 }
27040 if (pSampleRate) {
27041 *pSampleRate = 0;
27042 }
27043
27044 if (channels == 1) {
27045 bitsPerSample = 16;
27046 if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
27047 sampleRate = 48000;
27048 } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
27049 sampleRate = 44100;
27050 } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
27051 sampleRate = 22050;
27052 } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
27053 sampleRate = 11025;
27054 } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
27055 sampleRate = 96000;
27056 } else {
27057 bitsPerSample = 8;
27058 if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
27059 sampleRate = 48000;
27060 } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
27061 sampleRate = 44100;
27062 } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
27063 sampleRate = 22050;
27064 } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
27065 sampleRate = 11025;
27066 } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
27067 sampleRate = 96000;
27068 } else {
27070 }
27071 }
27072 } else {
27073 bitsPerSample = 16;
27074 if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
27075 sampleRate = 48000;
27076 } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
27077 sampleRate = 44100;
27078 } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
27079 sampleRate = 22050;
27080 } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
27081 sampleRate = 11025;
27082 } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
27083 sampleRate = 96000;
27084 } else {
27085 bitsPerSample = 8;
27086 if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
27087 sampleRate = 48000;
27088 } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
27089 sampleRate = 44100;
27090 } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
27091 sampleRate = 22050;
27092 } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
27093 sampleRate = 11025;
27094 } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
27095 sampleRate = 96000;
27096 } else {
27098 }
27099 }
27100 }
27101
27102 if (pBitsPerSample) {
27103 *pBitsPerSample = bitsPerSample;
27104 }
27105 if (pSampleRate) {
27106 *pSampleRate = sampleRate;
27107 }
27108
27109 return MA_SUCCESS;
27110}
27111
27112static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, MA_WAVEFORMATEX* pWF)
27113{
27114 ma_result result;
27115
27116 MA_ASSERT(pWF != NULL);
27117
27118 MA_ZERO_OBJECT(pWF);
27119 pWF->cbSize = sizeof(*pWF);
27120 pWF->wFormatTag = WAVE_FORMAT_PCM;
27121 pWF->nChannels = (WORD)channels;
27122 if (pWF->nChannels > 2) {
27123 pWF->nChannels = 2;
27124 }
27125
27126 result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec);
27127 if (result != MA_SUCCESS) {
27128 return result;
27129 }
27130
27131 pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8);
27132 pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec;
27133
27134 return MA_SUCCESS;
27135}
27136
27137static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo)
27138{
27139 WORD bitsPerSample;
27140 DWORD sampleRate;
27141 ma_result result;
27142
27143 MA_ASSERT(pContext != NULL);
27144 MA_ASSERT(pCaps != NULL);
27145 MA_ASSERT(pDeviceInfo != NULL);
27146
27147 /*
27148 Name / Description
27149
27150 Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking
27151 situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try
27152 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.
27153 */
27154
27155 /* Set the default to begin with. */
27156 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1);
27157
27158 /*
27159 Now try the registry. There's a few things to consider here:
27160 - The name GUID can be null, in which we case we just need to stick to the original 31 characters.
27161 - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters.
27162 - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The
27163 problem, however is that WASAPI and DirectSound use "<component> (<name>)" format (such as "Speakers (High Definition Audio)"),
27164 but WinMM does not specify the component name. From my admittedly limited testing, I've notice the component name seems to
27165 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
27166 name, and then concatenate the name from the registry.
27167 */
27168 if (!ma_is_guid_null(&pCaps->NameGuid)) {
27169 WCHAR guidStrW[256];
27170 if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) {
27171 char guidStr[256];
27172 char keyStr[1024];
27173 HKEY hKey;
27174
27175 WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE);
27176
27177 ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\");
27178 ma_strcat_s(keyStr, sizeof(keyStr), guidStr);
27179
27180 if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
27181 BYTE nameFromReg[512];
27182 DWORD nameFromRegSize = sizeof(nameFromReg);
27183 LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (BYTE*)nameFromReg, (DWORD*)&nameFromRegSize);
27184 ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey);
27185
27186 if (resultWin32 == ERROR_SUCCESS) {
27187 /* We have the value from the registry, so now we need to construct the name string. */
27188 char name[1024];
27189 if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) {
27190 char* nameBeg = ma_find_last_character(name, '(');
27191 if (nameBeg != NULL) {
27192 size_t leadingLen = (nameBeg - name);
27193 ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1);
27194
27195 /* The closing ")", if it can fit. */
27196 if (leadingLen + nameFromRegSize < sizeof(name)-1) {
27197 ma_strcat_s(name, sizeof(name), ")");
27198 }
27199
27200 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1);
27201 }
27202 }
27203 }
27204 }
27205 }
27206 }
27207
27208
27209 result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate);
27210 if (result != MA_SUCCESS) {
27211 return result;
27212 }
27213
27214 if (bitsPerSample == 8) {
27215 pDeviceInfo->nativeDataFormats[0].format = ma_format_u8;
27216 } else if (bitsPerSample == 16) {
27217 pDeviceInfo->nativeDataFormats[0].format = ma_format_s16;
27218 } else if (bitsPerSample == 24) {
27219 pDeviceInfo->nativeDataFormats[0].format = ma_format_s24;
27220 } else if (bitsPerSample == 32) {
27221 pDeviceInfo->nativeDataFormats[0].format = ma_format_s32;
27222 } else {
27224 }
27225 pDeviceInfo->nativeDataFormats[0].channels = pCaps->wChannels;
27226 pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate;
27227 pDeviceInfo->nativeDataFormats[0].flags = 0;
27228 pDeviceInfo->nativeDataFormatCount = 1;
27229
27230 return MA_SUCCESS;
27231}
27232
27233static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo)
27234{
27235 MA_WAVECAPSA caps;
27236
27237 MA_ASSERT(pContext != NULL);
27238 MA_ASSERT(pCaps != NULL);
27239 MA_ASSERT(pDeviceInfo != NULL);
27240
27241 MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
27242 caps.dwFormats = pCaps->dwFormats;
27243 caps.wChannels = pCaps->wChannels;
27244 caps.NameGuid = pCaps->NameGuid;
27245 return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
27246}
27247
27248static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo)
27249{
27250 MA_WAVECAPSA caps;
27251
27252 MA_ASSERT(pContext != NULL);
27253 MA_ASSERT(pCaps != NULL);
27254 MA_ASSERT(pDeviceInfo != NULL);
27255
27256 MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
27257 caps.dwFormats = pCaps->dwFormats;
27258 caps.wChannels = pCaps->wChannels;
27259 caps.NameGuid = pCaps->NameGuid;
27260 return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
27261}
27262
27263
27264static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
27265{
27266 UINT playbackDeviceCount;
27267 UINT captureDeviceCount;
27268 UINT iPlaybackDevice;
27269 UINT iCaptureDevice;
27270
27271 MA_ASSERT(pContext != NULL);
27272 MA_ASSERT(callback != NULL);
27273
27274 /* Playback. */
27275 playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)();
27276 for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) {
27277 MA_MMRESULT result;
27278 MA_WAVEOUTCAPS2A caps;
27279
27280 MA_ZERO_OBJECT(&caps);
27281
27282 result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps));
27283 if (result == MA_MMSYSERR_NOERROR) {
27284 ma_device_info deviceInfo;
27285
27286 MA_ZERO_OBJECT(&deviceInfo);
27287 deviceInfo.id.winmm = iPlaybackDevice;
27288
27289 /* The first enumerated device is the default device. */
27290 if (iPlaybackDevice == 0) {
27291 deviceInfo.isDefault = MA_TRUE;
27292 }
27293
27294 if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
27295 ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
27296 if (cbResult == MA_FALSE) {
27297 return MA_SUCCESS; /* Enumeration was stopped. */
27298 }
27299 }
27300 }
27301 }
27302
27303 /* Capture. */
27304 captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)();
27305 for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) {
27306 MA_MMRESULT result;
27307 MA_WAVEINCAPS2A caps;
27308
27309 MA_ZERO_OBJECT(&caps);
27310
27311 result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (MA_WAVEINCAPSA*)&caps, sizeof(caps));
27312 if (result == MA_MMSYSERR_NOERROR) {
27313 ma_device_info deviceInfo;
27314
27315 MA_ZERO_OBJECT(&deviceInfo);
27316 deviceInfo.id.winmm = iCaptureDevice;
27317
27318 /* The first enumerated device is the default device. */
27319 if (iCaptureDevice == 0) {
27320 deviceInfo.isDefault = MA_TRUE;
27321 }
27322
27323 if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
27324 ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
27325 if (cbResult == MA_FALSE) {
27326 return MA_SUCCESS; /* Enumeration was stopped. */
27327 }
27328 }
27329 }
27330 }
27331
27332 return MA_SUCCESS;
27333}
27334
27335static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
27336{
27337 UINT winMMDeviceID;
27338
27339 MA_ASSERT(pContext != NULL);
27340
27341 winMMDeviceID = 0;
27342 if (pDeviceID != NULL) {
27343 winMMDeviceID = (UINT)pDeviceID->winmm;
27344 }
27345
27346 pDeviceInfo->id.winmm = winMMDeviceID;
27347
27348 /* The first ID is the default device. */
27349 if (winMMDeviceID == 0) {
27350 pDeviceInfo->isDefault = MA_TRUE;
27351 }
27352
27353 if (deviceType == ma_device_type_playback) {
27354 MA_MMRESULT result;
27355 MA_WAVEOUTCAPS2A caps;
27356
27357 MA_ZERO_OBJECT(&caps);
27358
27359 result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps));
27360 if (result == MA_MMSYSERR_NOERROR) {
27361 return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo);
27362 }
27363 } else {
27364 MA_MMRESULT result;
27365 MA_WAVEINCAPS2A caps;
27366
27367 MA_ZERO_OBJECT(&caps);
27368
27369 result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (MA_WAVEINCAPSA*)&caps, sizeof(caps));
27370 if (result == MA_MMSYSERR_NOERROR) {
27371 return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo);
27372 }
27373 }
27374
27375 return MA_NO_DEVICE;
27376}
27377
27378
27379static ma_result ma_device_uninit__winmm(ma_device* pDevice)
27380{
27381 MA_ASSERT(pDevice != NULL);
27382
27383 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27384 ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);
27385 CloseHandle((HANDLE)pDevice->winmm.hEventCapture);
27386 }
27387
27388 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27389 ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);
27390 ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);
27391 CloseHandle((HANDLE)pDevice->winmm.hEventPlayback);
27392 }
27393
27394 ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
27395
27396 MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */
27397
27398 return MA_SUCCESS;
27399}
27400
27401static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
27402{
27403 /* WinMM has a minimum period size of 40ms. */
27404 ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate);
27405 ma_uint32 periodSizeInFrames;
27406
27407 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);
27408 if (periodSizeInFrames < minPeriodSizeInFrames) {
27409 periodSizeInFrames = minPeriodSizeInFrames;
27410 }
27411
27412 return periodSizeInFrames;
27413}
27414
27415static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
27416{
27417 const char* errorMsg = "";
27418 ma_result errorCode = MA_ERROR;
27419 ma_result result = MA_SUCCESS;
27420 ma_uint32 heapSize;
27421 UINT winMMDeviceIDPlayback = 0;
27422 UINT winMMDeviceIDCapture = 0;
27423
27424 MA_ASSERT(pDevice != NULL);
27425
27426 MA_ZERO_OBJECT(&pDevice->winmm);
27427
27428 if (pConfig->deviceType == ma_device_type_loopback) {
27430 }
27431
27432 /* No exclusive mode with WinMM. */
27433 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
27434 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
27436 }
27437
27438 if (pDescriptorPlayback->pDeviceID != NULL) {
27439 winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm;
27440 }
27441 if (pDescriptorCapture->pDeviceID != NULL) {
27442 winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm;
27443 }
27444
27445 /* The capture device needs to be initialized first. */
27446 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
27447 MA_WAVEINCAPSA caps;
27448 MA_WAVEFORMATEX wf;
27449 MA_MMRESULT resultMM;
27450
27451 /* We use an event to know when a new fragment needs to be enqueued. */
27452 pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL);
27453 if (pDevice->winmm.hEventCapture == NULL) {
27454 errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError());
27455 goto on_error;
27456 }
27457
27458 /* The format should be based on the device's actual format. */
27459 if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) {
27460 errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
27461 goto on_error;
27462 }
27463
27464 result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
27465 if (result != MA_SUCCESS) {
27466 errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
27467 goto on_error;
27468 }
27469
27470 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);
27471 if (resultMM != MA_MMSYSERR_NOERROR) {
27472 errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
27473 goto on_error;
27474 }
27475
27476 pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf);
27477 pDescriptorCapture->channels = wf.nChannels;
27478 pDescriptorCapture->sampleRate = wf.nSamplesPerSec;
27479 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
27480 pDescriptorCapture->periodCount = pDescriptorCapture->periodCount;
27481 pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
27482 }
27483
27484 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
27485 MA_WAVEOUTCAPSA caps;
27486 MA_WAVEFORMATEX wf;
27487 MA_MMRESULT resultMM;
27488
27489 /* We use an event to know when a new fragment needs to be enqueued. */
27490 pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL);
27491 if (pDevice->winmm.hEventPlayback == NULL) {
27492 errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError());
27493 goto on_error;
27494 }
27495
27496 /* The format should be based on the device's actual format. */
27497 if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) {
27498 errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
27499 goto on_error;
27500 }
27501
27502 result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
27503 if (result != MA_SUCCESS) {
27504 errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
27505 goto on_error;
27506 }
27507
27508 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);
27509 if (resultMM != MA_MMSYSERR_NOERROR) {
27510 errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
27511 goto on_error;
27512 }
27513
27514 pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf);
27515 pDescriptorPlayback->channels = wf.nChannels;
27516 pDescriptorPlayback->sampleRate = wf.nSamplesPerSec;
27517 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);
27518 pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount;
27519 pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
27520 }
27521
27522 /*
27523 The heap allocated data is allocated like so:
27524
27525 [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer]
27526 */
27527 heapSize = 0;
27528 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
27529 heapSize += sizeof(MA_WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels));
27530 }
27531 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
27532 heapSize += sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels));
27533 }
27534
27535 pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks);
27536 if (pDevice->winmm._pHeapData == NULL) {
27537 errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY;
27538 goto on_error;
27539 }
27540
27541 MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize);
27542
27543 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
27544 ma_uint32 iPeriod;
27545
27546 if (pConfig->deviceType == ma_device_type_capture) {
27547 pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
27548 pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount));
27549 } else {
27550 pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
27551 pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount));
27552 }
27553
27554 /* Prepare headers. */
27555 for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) {
27556 ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels);
27557
27558 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod));
27559 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes;
27560 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L;
27561 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L;
27562 ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR));
27563
27564 /*
27565 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
27566 it's unlocked and available for writing. A value of 1 means it's locked.
27567 */
27568 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0;
27569 }
27570 }
27571
27572 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
27573 ma_uint32 iPeriod;
27574
27575 if (pConfig->deviceType == ma_device_type_playback) {
27576 pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData;
27577 pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount);
27578 } else {
27579 pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount));
27580 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));
27581 }
27582
27583 /* Prepare headers. */
27584 for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) {
27585 ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels);
27586
27587 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod));
27588 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes;
27589 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L;
27590 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L;
27591 ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR));
27592
27593 /*
27594 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
27595 it's unlocked and available for writing. A value of 1 means it's locked.
27596 */
27597 ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0;
27598 }
27599 }
27600
27601 return MA_SUCCESS;
27602
27603on_error:
27604 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27605 if (pDevice->winmm.pWAVEHDRCapture != NULL) {
27606 ma_uint32 iPeriod;
27607 for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) {
27608 ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR));
27609 }
27610 }
27611
27612 ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);
27613 }
27614
27615 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27616 if (pDevice->winmm.pWAVEHDRCapture != NULL) {
27617 ma_uint32 iPeriod;
27618 for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) {
27619 ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR));
27620 }
27621 }
27622
27623 ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);
27624 }
27625
27626 ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
27627
27628 if (errorMsg != NULL && errorMsg[0] != '\0') {
27629 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "%s", errorMsg);
27630 }
27631
27632 return errorCode;
27633}
27634
27635static ma_result ma_device_start__winmm(ma_device* pDevice)
27636{
27637 MA_ASSERT(pDevice != NULL);
27638
27639 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27640 MA_MMRESULT resultMM;
27641 MA_WAVEHDR* pWAVEHDR;
27642 ma_uint32 iPeriod;
27643
27644 pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
27645
27646 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
27647 ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
27648
27649 /* 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. */
27650 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
27651 resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR));
27652 if (resultMM != MA_MMSYSERR_NOERROR) {
27653 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.");
27654 return ma_result_from_MMRESULT(resultMM);
27655 }
27656
27657 /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */
27658 pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */
27659 }
27660
27661 /* Capture devices need to be explicitly started, unlike playback devices. */
27662 resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);
27663 if (resultMM != MA_MMSYSERR_NOERROR) {
27664 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device.");
27665 return ma_result_from_MMRESULT(resultMM);
27666 }
27667 }
27668
27669 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27670 /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */
27671 }
27672
27673 return MA_SUCCESS;
27674}
27675
27676static ma_result ma_device_stop__winmm(ma_device* pDevice)
27677{
27678 MA_MMRESULT resultMM;
27679
27680 MA_ASSERT(pDevice != NULL);
27681
27682 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
27683 if (pDevice->winmm.hDeviceCapture == NULL) {
27684 return MA_INVALID_ARGS;
27685 }
27686
27687 resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);
27688 if (resultMM != MA_MMSYSERR_NOERROR) {
27689 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device.");
27690 }
27691 }
27692
27693 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
27694 ma_uint32 iPeriod;
27695 MA_WAVEHDR* pWAVEHDR;
27696
27697 if (pDevice->winmm.hDevicePlayback == NULL) {
27698 return MA_INVALID_ARGS;
27699 }
27700
27701 /* 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. */
27702 pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
27703 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) {
27704 if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */
27705 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
27706 break; /* An error occurred so just abandon ship and stop the device without draining. */
27707 }
27708
27709 pWAVEHDR[iPeriod].dwUser = 0;
27710 }
27711 }
27712
27713 resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);
27714 if (resultMM != MA_MMSYSERR_NOERROR) {
27715 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device.");
27716 }
27717 }
27718
27719 return MA_SUCCESS;
27720}
27721
27722static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
27723{
27724 ma_result result = MA_SUCCESS;
27725 MA_MMRESULT resultMM;
27726 ma_uint32 totalFramesWritten;
27727 MA_WAVEHDR* pWAVEHDR;
27728
27729 MA_ASSERT(pDevice != NULL);
27730 MA_ASSERT(pPCMFrames != NULL);
27731
27732 if (pFramesWritten != NULL) {
27733 *pFramesWritten = 0;
27734 }
27735
27736 pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
27737
27738 /* Keep processing as much data as possible. */
27739 totalFramesWritten = 0;
27740 while (totalFramesWritten < frameCount) {
27741 /* If the current header has some space available we need to write part of it. */
27742 if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */
27743 /*
27744 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
27745 write it out and move on to the next iteration.
27746 */
27748 ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback;
27749
27750 ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten));
27751 const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf);
27752 void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf);
27753 MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
27754
27755 pDevice->winmm.headerFramesConsumedPlayback += framesToCopy;
27756 totalFramesWritten += framesToCopy;
27757
27758 /* If we've consumed the buffer entirely we need to write it out to the device. */
27759 if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) {
27760 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */
27761 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
27762
27763 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
27764 ResetEvent((HANDLE)pDevice->winmm.hEventPlayback);
27765
27766 /* The device will be started here. */
27767 resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(MA_WAVEHDR));
27768 if (resultMM != MA_MMSYSERR_NOERROR) {
27769 result = ma_result_from_MMRESULT(resultMM);
27770 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed.");
27771 break;
27772 }
27773
27774 /* Make sure we move to the next header. */
27775 pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods;
27776 pDevice->winmm.headerFramesConsumedPlayback = 0;
27777 }
27778
27779 /* If at this point we have consumed the entire input buffer we can return. */
27780 MA_ASSERT(totalFramesWritten <= frameCount);
27781 if (totalFramesWritten == frameCount) {
27782 break;
27783 }
27784
27785 /* Getting here means there's more to process. */
27786 continue;
27787 }
27788
27789 /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */
27790 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
27791 result = MA_ERROR;
27792 break;
27793 }
27794
27795 /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
27796 if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & MA_WHDR_DONE) != 0) {
27797 pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */
27798 pDevice->winmm.headerFramesConsumedPlayback = 0;
27799 }
27800
27801 /* If the device has been stopped we need to break. */
27803 break;
27804 }
27805 }
27806
27807 if (pFramesWritten != NULL) {
27808 *pFramesWritten = totalFramesWritten;
27809 }
27810
27811 return result;
27812}
27813
27814static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
27815{
27816 ma_result result = MA_SUCCESS;
27817 MA_MMRESULT resultMM;
27818 ma_uint32 totalFramesRead;
27819 MA_WAVEHDR* pWAVEHDR;
27820
27821 MA_ASSERT(pDevice != NULL);
27822 MA_ASSERT(pPCMFrames != NULL);
27823
27824 if (pFramesRead != NULL) {
27825 *pFramesRead = 0;
27826 }
27827
27828 pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
27829
27830 /* Keep processing as much data as possible. */
27831 totalFramesRead = 0;
27832 while (totalFramesRead < frameCount) {
27833 /* If the current header has some space available we need to write part of it. */
27834 if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */
27835 /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */
27837 ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture;
27838
27839 ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead));
27840 const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf);
27841 void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf);
27842 MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
27843
27844 pDevice->winmm.headerFramesConsumedCapture += framesToCopy;
27845 totalFramesRead += framesToCopy;
27846
27847 /* If we've consumed the buffer entirely we need to add it back to the device. */
27848 if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) {
27849 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */
27850 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
27851
27852 /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
27853 ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
27854
27855 /* The device will be started here. */
27856 resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(MA_WAVEHDR));
27857 if (resultMM != MA_MMSYSERR_NOERROR) {
27858 result = ma_result_from_MMRESULT(resultMM);
27859 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed.");
27860 break;
27861 }
27862
27863 /* Make sure we move to the next header. */
27864 pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods;
27865 pDevice->winmm.headerFramesConsumedCapture = 0;
27866 }
27867
27868 /* If at this point we have filled the entire input buffer we can return. */
27869 MA_ASSERT(totalFramesRead <= frameCount);
27870 if (totalFramesRead == frameCount) {
27871 break;
27872 }
27873
27874 /* Getting here means there's more to process. */
27875 continue;
27876 }
27877
27878 /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */
27879 if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
27880 result = MA_ERROR;
27881 break;
27882 }
27883
27884 /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
27885 if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & MA_WHDR_DONE) != 0) {
27886 pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */
27887 pDevice->winmm.headerFramesConsumedCapture = 0;
27888 }
27889
27890 /* If the device has been stopped we need to break. */
27892 break;
27893 }
27894 }
27895
27896 if (pFramesRead != NULL) {
27897 *pFramesRead = totalFramesRead;
27898 }
27899
27900 return result;
27901}
27902
27903static ma_result ma_context_uninit__winmm(ma_context* pContext)
27904{
27905 MA_ASSERT(pContext != NULL);
27906 MA_ASSERT(pContext->backend == ma_backend_winmm);
27907
27908 ma_dlclose(ma_context_get_log(pContext), pContext->winmm.hWinMM);
27909 return MA_SUCCESS;
27910}
27911
27912static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
27913{
27914 MA_ASSERT(pContext != NULL);
27915
27916 (void)pConfig;
27917
27918 pContext->winmm.hWinMM = ma_dlopen(ma_context_get_log(pContext), "winmm.dll");
27919 if (pContext->winmm.hWinMM == NULL) {
27920 return MA_NO_BACKEND;
27921 }
27922
27923 pContext->winmm.waveOutGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetNumDevs");
27924 pContext->winmm.waveOutGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetDevCapsA");
27925 pContext->winmm.waveOutOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutOpen");
27926 pContext->winmm.waveOutClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutClose");
27927 pContext->winmm.waveOutPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutPrepareHeader");
27928 pContext->winmm.waveOutUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutUnprepareHeader");
27929 pContext->winmm.waveOutWrite = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutWrite");
27930 pContext->winmm.waveOutReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutReset");
27931 pContext->winmm.waveInGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetNumDevs");
27932 pContext->winmm.waveInGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetDevCapsA");
27933 pContext->winmm.waveInOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInOpen");
27934 pContext->winmm.waveInClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInClose");
27935 pContext->winmm.waveInPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInPrepareHeader");
27936 pContext->winmm.waveInUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInUnprepareHeader");
27937 pContext->winmm.waveInAddBuffer = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInAddBuffer");
27938 pContext->winmm.waveInStart = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInStart");
27939 pContext->winmm.waveInReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInReset");
27940
27941 pCallbacks->onContextInit = ma_context_init__winmm;
27942 pCallbacks->onContextUninit = ma_context_uninit__winmm;
27943 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm;
27944 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm;
27945 pCallbacks->onDeviceInit = ma_device_init__winmm;
27946 pCallbacks->onDeviceUninit = ma_device_uninit__winmm;
27947 pCallbacks->onDeviceStart = ma_device_start__winmm;
27948 pCallbacks->onDeviceStop = ma_device_stop__winmm;
27949 pCallbacks->onDeviceRead = ma_device_read__winmm;
27950 pCallbacks->onDeviceWrite = ma_device_write__winmm;
27951 pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */
27952
27953 return MA_SUCCESS;
27954}
27955#endif
27956
27957
27958
27959
27960/******************************************************************************
27961
27962ALSA Backend
27963
27964******************************************************************************/
27965#ifdef MA_HAS_ALSA
27966
27967#include <poll.h> /* poll(), struct pollfd */
27968#include <sys/eventfd.h> /* eventfd() */
27969
27970#ifdef MA_NO_RUNTIME_LINKING
27971
27972/* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */
27973#if !defined(__cplusplus)
27974 #if defined(__STRICT_ANSI__)
27975 #if !defined(inline)
27976 #define inline __inline__ __attribute__((always_inline))
27977 #define MA_INLINE_DEFINED
27978 #endif
27979 #endif
27980#endif
27981#include <alsa/asoundlib.h>
27982#if defined(MA_INLINE_DEFINED)
27983 #undef inline
27984 #undef MA_INLINE_DEFINED
27985#endif
27986
27987typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t;
27988typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t;
27989typedef snd_pcm_stream_t ma_snd_pcm_stream_t;
27990typedef snd_pcm_format_t ma_snd_pcm_format_t;
27991typedef snd_pcm_access_t ma_snd_pcm_access_t;
27992typedef snd_pcm_t ma_snd_pcm_t;
27993typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
27994typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
27995typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
27996typedef snd_pcm_info_t ma_snd_pcm_info_t;
27997typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t;
27998typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t;
27999typedef snd_pcm_state_t ma_snd_pcm_state_t;
28000
28001/* snd_pcm_state_t */
28002#define MA_SND_PCM_STATE_XRUN SND_PCM_STATE_XRUN
28003
28004/* snd_pcm_stream_t */
28005#define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK
28006#define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE
28007
28008/* snd_pcm_format_t */
28009#define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN
28010#define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8
28011#define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE
28012#define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE
28013#define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE
28014#define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE
28015#define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE
28016#define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE
28017#define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE
28018#define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE
28019#define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE
28020#define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE
28021#define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW
28022#define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW
28023#define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE
28024#define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE
28025
28026/* ma_snd_pcm_access_t */
28027#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED
28028#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED
28029#define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX
28030#define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED
28031#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED
28032
28033/* Channel positions. */
28034#define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN
28035#define MA_SND_CHMAP_NA SND_CHMAP_NA
28036#define MA_SND_CHMAP_MONO SND_CHMAP_MONO
28037#define MA_SND_CHMAP_FL SND_CHMAP_FL
28038#define MA_SND_CHMAP_FR SND_CHMAP_FR
28039#define MA_SND_CHMAP_RL SND_CHMAP_RL
28040#define MA_SND_CHMAP_RR SND_CHMAP_RR
28041#define MA_SND_CHMAP_FC SND_CHMAP_FC
28042#define MA_SND_CHMAP_LFE SND_CHMAP_LFE
28043#define MA_SND_CHMAP_SL SND_CHMAP_SL
28044#define MA_SND_CHMAP_SR SND_CHMAP_SR
28045#define MA_SND_CHMAP_RC SND_CHMAP_RC
28046#define MA_SND_CHMAP_FLC SND_CHMAP_FLC
28047#define MA_SND_CHMAP_FRC SND_CHMAP_FRC
28048#define MA_SND_CHMAP_RLC SND_CHMAP_RLC
28049#define MA_SND_CHMAP_RRC SND_CHMAP_RRC
28050#define MA_SND_CHMAP_FLW SND_CHMAP_FLW
28051#define MA_SND_CHMAP_FRW SND_CHMAP_FRW
28052#define MA_SND_CHMAP_FLH SND_CHMAP_FLH
28053#define MA_SND_CHMAP_FCH SND_CHMAP_FCH
28054#define MA_SND_CHMAP_FRH SND_CHMAP_FRH
28055#define MA_SND_CHMAP_TC SND_CHMAP_TC
28056#define MA_SND_CHMAP_TFL SND_CHMAP_TFL
28057#define MA_SND_CHMAP_TFR SND_CHMAP_TFR
28058#define MA_SND_CHMAP_TFC SND_CHMAP_TFC
28059#define MA_SND_CHMAP_TRL SND_CHMAP_TRL
28060#define MA_SND_CHMAP_TRR SND_CHMAP_TRR
28061#define MA_SND_CHMAP_TRC SND_CHMAP_TRC
28062#define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC
28063#define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC
28064#define MA_SND_CHMAP_TSL SND_CHMAP_TSL
28065#define MA_SND_CHMAP_TSR SND_CHMAP_TSR
28066#define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE
28067#define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE
28068#define MA_SND_CHMAP_BC SND_CHMAP_BC
28069#define MA_SND_CHMAP_BLC SND_CHMAP_BLC
28070#define MA_SND_CHMAP_BRC SND_CHMAP_BRC
28071
28072/* Open mode flags. */
28073#define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE
28074#define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS
28075#define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT
28076#else
28077#include <errno.h> /* For EPIPE, etc. */
28078typedef unsigned long ma_snd_pcm_uframes_t;
28079typedef long ma_snd_pcm_sframes_t;
28080typedef int ma_snd_pcm_stream_t;
28081typedef int ma_snd_pcm_format_t;
28082typedef int ma_snd_pcm_access_t;
28083typedef int ma_snd_pcm_state_t;
28084typedef struct ma_snd_pcm_t ma_snd_pcm_t;
28085typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
28086typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
28087typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
28088typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t;
28089typedef struct
28090{
28091 void* addr;
28092 unsigned int first;
28093 unsigned int step;
28094} ma_snd_pcm_channel_area_t;
28095typedef struct
28096{
28097 unsigned int channels;
28098 unsigned int pos[1];
28099} ma_snd_pcm_chmap_t;
28100
28101/* snd_pcm_state_t */
28102#define MA_SND_PCM_STATE_OPEN 0
28103#define MA_SND_PCM_STATE_SETUP 1
28104#define MA_SND_PCM_STATE_PREPARED 2
28105#define MA_SND_PCM_STATE_RUNNING 3
28106#define MA_SND_PCM_STATE_XRUN 4
28107#define MA_SND_PCM_STATE_DRAINING 5
28108#define MA_SND_PCM_STATE_PAUSED 6
28109#define MA_SND_PCM_STATE_SUSPENDED 7
28110#define MA_SND_PCM_STATE_DISCONNECTED 8
28111
28112/* snd_pcm_stream_t */
28113#define MA_SND_PCM_STREAM_PLAYBACK 0
28114#define MA_SND_PCM_STREAM_CAPTURE 1
28115
28116/* snd_pcm_format_t */
28117#define MA_SND_PCM_FORMAT_UNKNOWN -1
28118#define MA_SND_PCM_FORMAT_U8 1
28119#define MA_SND_PCM_FORMAT_S16_LE 2
28120#define MA_SND_PCM_FORMAT_S16_BE 3
28121#define MA_SND_PCM_FORMAT_S24_LE 6
28122#define MA_SND_PCM_FORMAT_S24_BE 7
28123#define MA_SND_PCM_FORMAT_S32_LE 10
28124#define MA_SND_PCM_FORMAT_S32_BE 11
28125#define MA_SND_PCM_FORMAT_FLOAT_LE 14
28126#define MA_SND_PCM_FORMAT_FLOAT_BE 15
28127#define MA_SND_PCM_FORMAT_FLOAT64_LE 16
28128#define MA_SND_PCM_FORMAT_FLOAT64_BE 17
28129#define MA_SND_PCM_FORMAT_MU_LAW 20
28130#define MA_SND_PCM_FORMAT_A_LAW 21
28131#define MA_SND_PCM_FORMAT_S24_3LE 32
28132#define MA_SND_PCM_FORMAT_S24_3BE 33
28133
28134/* snd_pcm_access_t */
28135#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0
28136#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1
28137#define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2
28138#define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3
28139#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4
28140
28141/* Channel positions. */
28142#define MA_SND_CHMAP_UNKNOWN 0
28143#define MA_SND_CHMAP_NA 1
28144#define MA_SND_CHMAP_MONO 2
28145#define MA_SND_CHMAP_FL 3
28146#define MA_SND_CHMAP_FR 4
28147#define MA_SND_CHMAP_RL 5
28148#define MA_SND_CHMAP_RR 6
28149#define MA_SND_CHMAP_FC 7
28150#define MA_SND_CHMAP_LFE 8
28151#define MA_SND_CHMAP_SL 9
28152#define MA_SND_CHMAP_SR 10
28153#define MA_SND_CHMAP_RC 11
28154#define MA_SND_CHMAP_FLC 12
28155#define MA_SND_CHMAP_FRC 13
28156#define MA_SND_CHMAP_RLC 14
28157#define MA_SND_CHMAP_RRC 15
28158#define MA_SND_CHMAP_FLW 16
28159#define MA_SND_CHMAP_FRW 17
28160#define MA_SND_CHMAP_FLH 18
28161#define MA_SND_CHMAP_FCH 19
28162#define MA_SND_CHMAP_FRH 20
28163#define MA_SND_CHMAP_TC 21
28164#define MA_SND_CHMAP_TFL 22
28165#define MA_SND_CHMAP_TFR 23
28166#define MA_SND_CHMAP_TFC 24
28167#define MA_SND_CHMAP_TRL 25
28168#define MA_SND_CHMAP_TRR 26
28169#define MA_SND_CHMAP_TRC 27
28170#define MA_SND_CHMAP_TFLC 28
28171#define MA_SND_CHMAP_TFRC 29
28172#define MA_SND_CHMAP_TSL 30
28173#define MA_SND_CHMAP_TSR 31
28174#define MA_SND_CHMAP_LLFE 32
28175#define MA_SND_CHMAP_RLFE 33
28176#define MA_SND_CHMAP_BC 34
28177#define MA_SND_CHMAP_BLC 35
28178#define MA_SND_CHMAP_BRC 36
28179
28180/* Open mode flags. */
28181#define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000
28182#define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000
28183#define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000
28184#endif
28185
28186typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode);
28187typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm);
28188typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void);
28189typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
28190typedef 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);
28191typedef 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);
28192typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask);
28193typedef int (* ma_snd_pcm_hw_params_set_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);
28194typedef 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);
28195typedef 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);
28196typedef 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);
28197typedef 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);
28198typedef 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);
28199typedef int (* ma_snd_pcm_hw_params_set_rate_minmax_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *min, int *mindir, unsigned int *max, int *maxdir);
28200typedef 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);
28201typedef 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);
28202typedef 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);
28203typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);
28204typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
28205typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
28206typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
28207typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
28208typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
28209typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
28210typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val);
28211typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
28212typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access);
28213typedef 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);
28214typedef int (* ma_snd_pcm_hw_params_test_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);
28215typedef 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);
28216typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
28217typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void);
28218typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
28219typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val);
28220typedef 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);
28221typedef 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);
28222typedef 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);
28223typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
28224typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void);
28225typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val);
28226typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm);
28227typedef ma_snd_pcm_state_t (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm);
28228typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm);
28229typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm);
28230typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm);
28231typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm);
28232typedef int (* ma_snd_pcm_reset_proc) (ma_snd_pcm_t *pcm);
28233typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints);
28234typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id);
28235typedef int (* ma_snd_card_get_index_proc) (const char *name);
28236typedef int (* ma_snd_device_name_free_hint_proc) (void **hints);
28237typedef 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);
28238typedef 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);
28239typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent);
28240typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size);
28241typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size);
28242typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm);
28243typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm);
28244typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout);
28245typedef int (* ma_snd_pcm_nonblock_proc) (ma_snd_pcm_t *pcm, int nonblock);
28246typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info);
28247typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void);
28248typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info);
28249typedef int (* ma_snd_pcm_poll_descriptors_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space);
28250typedef int (* ma_snd_pcm_poll_descriptors_count_proc) (ma_snd_pcm_t *pcm);
28251typedef int (* ma_snd_pcm_poll_descriptors_revents_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents);
28252typedef int (* ma_snd_config_update_free_global_proc) (void);
28253
28254/* This array specifies each of the common devices that can be used for both playback and capture. */
28255static const char* g_maCommonDeviceNamesALSA[] = {
28256 "default",
28257 "null",
28258 "pulse",
28259 "jack"
28260};
28261
28262/* This array allows us to blacklist specific playback devices. */
28263static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = {
28264 ""
28265};
28266
28267/* This array allows us to blacklist specific capture devices. */
28268static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = {
28269 ""
28270};
28271
28272
28273static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format)
28274{
28275 ma_snd_pcm_format_t ALSAFormats[] = {
28276 MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */
28277 MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */
28278 MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */
28279 MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */
28280 MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */
28281 MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */
28282 };
28283
28284 if (ma_is_big_endian()) {
28285 ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN;
28286 ALSAFormats[1] = MA_SND_PCM_FORMAT_U8;
28287 ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE;
28288 ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE;
28289 ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE;
28290 ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE;
28291 }
28292
28293 return ALSAFormats[format];
28294}
28295
28296static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA)
28297{
28298 if (ma_is_little_endian()) {
28299 switch (formatALSA) {
28300 case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16;
28301 case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24;
28302 case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32;
28303 case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32;
28304 default: break;
28305 }
28306 } else {
28307 switch (formatALSA) {
28308 case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16;
28309 case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24;
28310 case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32;
28311 case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32;
28312 default: break;
28313 }
28314 }
28315
28316 /* Endian agnostic. */
28317 switch (formatALSA) {
28318 case MA_SND_PCM_FORMAT_U8: return ma_format_u8;
28319 default: return ma_format_unknown;
28320 }
28321}
28322
28323static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos)
28324{
28325 switch (alsaChannelPos)
28326 {
28327 case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO;
28328 case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT;
28329 case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT;
28330 case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT;
28331 case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT;
28332 case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER;
28333 case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE;
28334 case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT;
28335 case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT;
28336 case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER;
28337 case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER;
28338 case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER;
28339 case MA_SND_CHMAP_RLC: return 0;
28340 case MA_SND_CHMAP_RRC: return 0;
28341 case MA_SND_CHMAP_FLW: return 0;
28342 case MA_SND_CHMAP_FRW: return 0;
28343 case MA_SND_CHMAP_FLH: return 0;
28344 case MA_SND_CHMAP_FCH: return 0;
28345 case MA_SND_CHMAP_FRH: return 0;
28346 case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER;
28347 case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT;
28348 case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT;
28349 case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER;
28350 case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT;
28351 case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT;
28352 case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER;
28353 default: break;
28354 }
28355
28356 return 0;
28357}
28358
28359static ma_bool32 ma_is_common_device_name__alsa(const char* name)
28360{
28361 size_t iName;
28362 for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) {
28363 if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) {
28364 return MA_TRUE;
28365 }
28366 }
28367
28368 return MA_FALSE;
28369}
28370
28371
28372static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name)
28373{
28374 size_t iName;
28375 for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) {
28376 if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) {
28377 return MA_TRUE;
28378 }
28379 }
28380
28381 return MA_FALSE;
28382}
28383
28384static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name)
28385{
28386 size_t iName;
28387 for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) {
28388 if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) {
28389 return MA_TRUE;
28390 }
28391 }
28392
28393 return MA_FALSE;
28394}
28395
28396static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name)
28397{
28398 if (deviceType == ma_device_type_playback) {
28399 return ma_is_playback_device_blacklisted__alsa(name);
28400 } else {
28401 return ma_is_capture_device_blacklisted__alsa(name);
28402 }
28403}
28404
28405
28406static const char* ma_find_char(const char* str, char c, int* index)
28407{
28408 int i = 0;
28409 for (;;) {
28410 if (str[i] == '\0') {
28411 if (index) *index = -1;
28412 return NULL;
28413 }
28414
28415 if (str[i] == c) {
28416 if (index) *index = i;
28417 return str + i;
28418 }
28419
28420 i += 1;
28421 }
28422
28423 /* Should never get here, but treat it as though the character was not found to make me feel better inside. */
28424 if (index) *index = -1;
28425 return NULL;
28426}
28427
28428static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid)
28429{
28430 /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */
28431
28432 int commaPos;
28433 const char* dev;
28434 int i;
28435
28436 if (hwid == NULL) {
28437 return MA_FALSE;
28438 }
28439
28440 if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') {
28441 return MA_FALSE;
28442 }
28443
28444 hwid += 3;
28445
28446 dev = ma_find_char(hwid, ',', &commaPos);
28447 if (dev == NULL) {
28448 return MA_FALSE;
28449 } else {
28450 dev += 1; /* Skip past the ",". */
28451 }
28452
28453 /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */
28454 for (i = 0; i < commaPos; ++i) {
28455 if (hwid[i] < '0' || hwid[i] > '9') {
28456 return MA_FALSE;
28457 }
28458 }
28459
28460 /* Check if everything after the "," is numeric. If not, return false. */
28461 i = 0;
28462 while (dev[i] != '\0') {
28463 if (dev[i] < '0' || dev[i] > '9') {
28464 return MA_FALSE;
28465 }
28466 i += 1;
28467 }
28468
28469 return MA_TRUE;
28470}
28471
28472static 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. */
28473{
28474 /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */
28475
28476 int colonPos;
28477 int commaPos;
28478 char card[256];
28479 const char* dev;
28480 int cardIndex;
28481
28482 if (dst == NULL) {
28483 return -1;
28484 }
28485 if (dstSize < 7) {
28486 return -1; /* Absolute minimum size of the output buffer is 7 bytes. */
28487 }
28488
28489 *dst = '\0'; /* Safety. */
28490 if (src == NULL) {
28491 return -1;
28492 }
28493
28494 /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */
28495 if (ma_is_device_name_in_hw_format__alsa(src)) {
28496 return ma_strcpy_s(dst, dstSize, src);
28497 }
28498
28499 src = ma_find_char(src, ':', &colonPos);
28500 if (src == NULL) {
28501 return -1; /* Couldn't find a colon */
28502 }
28503
28504 dev = ma_find_char(src, ',', &commaPos);
28505 if (dev == NULL) {
28506 dev = "0";
28507 ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */
28508 } else {
28509 dev = dev + 5; /* +5 = ",DEV=" */
28510 ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */
28511 }
28512
28513 cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card);
28514 if (cardIndex < 0) {
28515 return -2; /* Failed to retrieve the card index. */
28516 }
28517
28518
28519 /* Construction. */
28520 dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':';
28521 if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) {
28522 return -3;
28523 }
28524 if (ma_strcat_s(dst, dstSize, ",") != 0) {
28525 return -3;
28526 }
28527 if (ma_strcat_s(dst, dstSize, dev) != 0) {
28528 return -3;
28529 }
28530
28531 return 0;
28532}
28533
28534static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID)
28535{
28536 ma_uint32 i;
28537
28538 MA_ASSERT(pHWID != NULL);
28539
28540 for (i = 0; i < count; ++i) {
28541 if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) {
28542 return MA_TRUE;
28543 }
28544 }
28545
28546 return MA_FALSE;
28547}
28548
28549
28550static 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)
28551{
28552 ma_snd_pcm_t* pPCM;
28553 ma_snd_pcm_stream_t stream;
28554
28555 MA_ASSERT(pContext != NULL);
28556 MA_ASSERT(ppPCM != NULL);
28557
28558 *ppPCM = NULL;
28559 pPCM = NULL;
28560
28561 stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE;
28562
28563 if (pDeviceID == NULL) {
28564 ma_bool32 isDeviceOpen;
28565 size_t i;
28566
28567 /*
28568 We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes
28569 me feel better to try as hard as we can get to get _something_ working.
28570 */
28571 const char* defaultDeviceNames[] = {
28572 "default",
28573 NULL,
28574 NULL,
28575 NULL,
28576 NULL,
28577 NULL,
28578 NULL
28579 };
28580
28581 if (shareMode == ma_share_mode_exclusive) {
28582 defaultDeviceNames[1] = "hw";
28583 defaultDeviceNames[2] = "hw:0";
28584 defaultDeviceNames[3] = "hw:0,0";
28585 } else {
28586 if (deviceType == ma_device_type_playback) {
28587 defaultDeviceNames[1] = "dmix";
28588 defaultDeviceNames[2] = "dmix:0";
28589 defaultDeviceNames[3] = "dmix:0,0";
28590 } else {
28591 defaultDeviceNames[1] = "dsnoop";
28592 defaultDeviceNames[2] = "dsnoop:0";
28593 defaultDeviceNames[3] = "dsnoop:0,0";
28594 }
28595 defaultDeviceNames[4] = "hw";
28596 defaultDeviceNames[5] = "hw:0";
28597 defaultDeviceNames[6] = "hw:0,0";
28598 }
28599
28600 isDeviceOpen = MA_FALSE;
28601 for (i = 0; i < ma_countof(defaultDeviceNames); ++i) {
28602 if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') {
28603 if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) {
28604 isDeviceOpen = MA_TRUE;
28605 break;
28606 }
28607 }
28608 }
28609
28610 if (!isDeviceOpen) {
28611 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.");
28613 }
28614 } else {
28615 /*
28616 We're trying to open a specific device. There's a few things to consider here:
28617
28618 miniaudio recognizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When
28619 an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it
28620 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").
28621 */
28622
28623 /* May end up needing to make small adjustments to the ID, so make a copy. */
28624 ma_device_id deviceID = *pDeviceID;
28625 int resultALSA = -ENODEV;
28626
28627 if (deviceID.alsa[0] != ':') {
28628 /* The ID is not in ":0,0" format. Use the ID exactly as-is. */
28629 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode);
28630 } else {
28631 char hwid[256];
28632
28633 /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */
28634 if (deviceID.alsa[1] == '\0') {
28635 deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */
28636 }
28637
28638 if (shareMode == ma_share_mode_shared) {
28639 if (deviceType == ma_device_type_playback) {
28640 ma_strcpy_s(hwid, sizeof(hwid), "dmix");
28641 } else {
28642 ma_strcpy_s(hwid, sizeof(hwid), "dsnoop");
28643 }
28644
28645 if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
28646 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
28647 }
28648 }
28649
28650 /* 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. */
28651 if (resultALSA != 0) {
28652 ma_strcpy_s(hwid, sizeof(hwid), "hw");
28653 if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
28654 resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
28655 }
28656 }
28657 }
28658
28659 if (resultALSA < 0) {
28660 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed.");
28661 return ma_result_from_errno(-resultALSA);
28662 }
28663 }
28664
28665 *ppPCM = pPCM;
28666 return MA_SUCCESS;
28667}
28668
28669
28670static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
28671{
28672 int resultALSA;
28673 ma_bool32 cbResult = MA_TRUE;
28674 char** ppDeviceHints;
28675 ma_device_id* pUniqueIDs = NULL;
28676 ma_uint32 uniqueIDCount = 0;
28677 char** ppNextDeviceHint;
28678
28679 MA_ASSERT(pContext != NULL);
28680 MA_ASSERT(callback != NULL);
28681
28682 ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock);
28683
28684 resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints);
28685 if (resultALSA < 0) {
28686 ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
28687 return ma_result_from_errno(-resultALSA);
28688 }
28689
28690 ppNextDeviceHint = ppDeviceHints;
28691 while (*ppNextDeviceHint != NULL) {
28692 char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME");
28693 char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC");
28694 char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID");
28696 ma_bool32 stopEnumeration = MA_FALSE;
28697 char hwid[sizeof(pUniqueIDs->alsa)];
28698 ma_device_info deviceInfo;
28699
28700 if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) {
28701 deviceType = ma_device_type_playback;
28702 }
28703 if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) {
28704 deviceType = ma_device_type_capture;
28705 }
28706
28707 if (NAME != NULL) {
28708 if (pContext->alsa.useVerboseDeviceEnumeration) {
28709 /* Verbose mode. Use the name exactly as-is. */
28710 ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
28711 } else {
28712 /* Simplified mode. Use ":%d,%d" format. */
28713 if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) {
28714 /*
28715 At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the
28716 plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device
28717 initialization time and is used as an indicator to try to use the most appropriate plugin depending on the
28718 device type and sharing mode.
28719 */
28720 char* dst = hwid;
28721 char* src = hwid+2;
28722 while ((*dst++ = *src++));
28723 } else {
28724 /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */
28725 ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
28726 }
28727
28728 if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) {
28729 goto next_device; /* The device has already been enumerated. Move on to the next one. */
28730 } else {
28731 /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */
28732 size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1);
28733 ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks);
28734 if (pNewUniqueIDs == NULL) {
28735 goto next_device; /* Failed to allocate memory. */
28736 }
28737
28738 pUniqueIDs = pNewUniqueIDs;
28739 MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid));
28740 uniqueIDCount += 1;
28741 }
28742 }
28743 } else {
28744 MA_ZERO_MEMORY(hwid, sizeof(hwid));
28745 }
28746
28747 MA_ZERO_OBJECT(&deviceInfo);
28748 ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1);
28749
28750 /*
28751 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
28752 just use the name of "default" as the indicator.
28753 */
28754 if (ma_strcmp(deviceInfo.id.alsa, "default") == 0) {
28755 deviceInfo.isDefault = MA_TRUE;
28756 }
28757
28758
28759 /*
28760 DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose
28761 device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish
28762 between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the
28763 description.
28764
28765 The value in DESC seems to be split into two lines, with the first line being the name of the device and the
28766 second line being a description of the device. I don't like having the description be across two lines because
28767 it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line
28768 being put into parentheses. In simplified mode I'm just stripping the second line entirely.
28769 */
28770 if (DESC != NULL) {
28771 int lfPos;
28772 const char* line2 = ma_find_char(DESC, '\n', &lfPos);
28773 if (line2 != NULL) {
28774 line2 += 1; /* Skip past the new-line character. */
28775
28776 if (pContext->alsa.useVerboseDeviceEnumeration) {
28777 /* Verbose mode. Put the second line in brackets. */
28778 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
28779 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " (");
28780 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2);
28781 ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")");
28782 } else {
28783 /* Simplified mode. Strip the second line entirely. */
28784 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
28785 }
28786 } else {
28787 /* There's no second line. Just copy the whole description. */
28788 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1);
28789 }
28790 }
28791
28792 if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) {
28793 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
28794 }
28795
28796 /*
28797 Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback
28798 again for the other device type in this case. We do this for known devices and where the IOID hint is NULL, which
28799 means both Input and Output.
28800 */
28801 if (cbResult) {
28802 if (ma_is_common_device_name__alsa(NAME) || IOID == NULL) {
28803 if (deviceType == ma_device_type_playback) {
28804 if (!ma_is_capture_device_blacklisted__alsa(NAME)) {
28805 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
28806 }
28807 } else {
28808 if (!ma_is_playback_device_blacklisted__alsa(NAME)) {
28809 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
28810 }
28811 }
28812 }
28813 }
28814
28815 if (cbResult == MA_FALSE) {
28816 stopEnumeration = MA_TRUE;
28817 }
28818
28819 next_device:
28820 free(NAME);
28821 free(DESC);
28822 free(IOID);
28823 ppNextDeviceHint += 1;
28824
28825 /* We need to stop enumeration if the callback returned false. */
28826 if (stopEnumeration) {
28827 break;
28828 }
28829 }
28830
28831 ma_free(pUniqueIDs, &pContext->allocationCallbacks);
28832 ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);
28833
28834 ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
28835
28836 return MA_SUCCESS;
28837}
28838
28839
28840typedef struct
28841{
28842 ma_device_type deviceType;
28843 const ma_device_id* pDeviceID;
28844 ma_share_mode shareMode;
28845 ma_device_info* pDeviceInfo;
28846 ma_bool32 foundDevice;
28847} ma_context_get_device_info_enum_callback_data__alsa;
28848
28849static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData)
28850{
28851 ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData;
28852 MA_ASSERT(pData != NULL);
28853
28854 (void)pContext;
28855
28856 if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
28857 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
28858 pData->foundDevice = MA_TRUE;
28859 } else {
28860 if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) {
28861 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
28862 pData->foundDevice = MA_TRUE;
28863 }
28864 }
28865
28866 /* Keep enumerating until we have found the device. */
28867 return !pData->foundDevice;
28868}
28869
28870static 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)
28871{
28872 MA_ASSERT(pPCM != NULL);
28873 MA_ASSERT(pHWParams != NULL);
28874 MA_ASSERT(pDeviceInfo != NULL);
28875
28876 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) {
28877 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
28878 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
28879 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
28880 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
28881 pDeviceInfo->nativeDataFormatCount += 1;
28882 }
28883}
28884
28885static 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)
28886{
28887 ma_uint32 iSampleRate;
28888 unsigned int minSampleRate;
28889 unsigned int maxSampleRate;
28890 int sampleRateDir; /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */
28891
28892 /* There could be a range. */
28893 ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir);
28894 ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir);
28895
28896 /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculous. */
28897 minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);
28898 maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);
28899
28900 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {
28901 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];
28902
28903 if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {
28904 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo);
28905 }
28906 }
28907
28908 /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */
28909 if (!ma_is_standard_sample_rate(minSampleRate)) {
28910 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo);
28911 }
28912
28913 if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) {
28914 ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo);
28915 }
28916}
28917
28918static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
28919{
28920 ma_context_get_device_info_enum_callback_data__alsa data;
28921 ma_result result;
28922 int resultALSA;
28923 ma_snd_pcm_t* pPCM;
28924 ma_snd_pcm_hw_params_t* pHWParams;
28925 ma_uint32 iFormat;
28926 ma_uint32 iChannel;
28927
28928 MA_ASSERT(pContext != NULL);
28929
28930 /* We just enumerate to find basic information about the device. */
28931 data.deviceType = deviceType;
28932 data.pDeviceID = pDeviceID;
28933 data.pDeviceInfo = pDeviceInfo;
28934 data.foundDevice = MA_FALSE;
28935 result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data);
28936 if (result != MA_SUCCESS) {
28937 return result;
28938 }
28939
28940 if (!data.foundDevice) {
28941 return MA_NO_DEVICE;
28942 }
28943
28944 if (ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
28945 pDeviceInfo->isDefault = MA_TRUE;
28946 }
28947
28948 /* For detailed info we need to open the device. */
28949 result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM);
28950 if (result != MA_SUCCESS) {
28951 return result;
28952 }
28953
28954 /* We need to initialize a HW parameters object in order to know what formats are supported. */
28955 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);
28956 if (pHWParams == NULL) {
28957 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
28958 return MA_OUT_OF_MEMORY;
28959 }
28960
28961 resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
28962 if (resultALSA < 0) {
28963 ma_free(pHWParams, &pContext->allocationCallbacks);
28964 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
28965 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.");
28966 return ma_result_from_errno(-resultALSA);
28967 }
28968
28969 /*
28970 Some ALSA devices can support many permutations of formats, channels and rates. We only support
28971 a fixed number of permutations which means we need to employ some strategies to ensure the best
28972 combinations are returned. An example is the "pulse" device which can do its own data conversion
28973 in software and as a result can support any combination of format, channels and rate.
28974
28975 We want to ensure that the first data formats are the best. We have a list of favored sample
28976 formats and sample rates, so these will be the basis of our iteration.
28977 */
28978
28979 /* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */
28980 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
28981 ma_format format = g_maFormatPriorities[iFormat];
28982
28983 /*
28984 For each format we need to make sure we reset the configuration space so we don't return
28985 channel counts and rates that aren't compatible with a format.
28986 */
28987 ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
28988
28989 /* Test the format first. If this fails it means the format is not supported and we can skip it. */
28990 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) {
28991 /* The format is supported. */
28992 unsigned int minChannels;
28993 unsigned int maxChannels;
28994
28995 /*
28996 The configuration space needs to be restricted to this format so we can get an accurate
28997 picture of which sample rates and channel counts are support with this format.
28998 */
28999 ((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));
29000
29001 /* Now we need to check for supported channels. */
29002 ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels);
29003 ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels);
29004
29005 if (minChannels > MA_MAX_CHANNELS) {
29006 continue; /* Too many channels. */
29007 }
29008 if (maxChannels < MA_MIN_CHANNELS) {
29009 continue; /* Not enough channels. */
29010 }
29011
29012 /*
29013 Make sure the channel count is clamped. This is mainly intended for the max channels
29014 because some devices can report an unbound maximum.
29015 */
29016 minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
29017 maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
29018
29019 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
29020 /* 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. */
29021 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. */
29022 } else {
29023 /* The device only supports a specific set of channels. We need to iterate over all of them. */
29024 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
29025 /* Test the channel before applying it to the configuration space. */
29026 unsigned int channels = iChannel;
29027
29028 /* Make sure our channel range is reset before testing again or else we'll always fail the test. */
29029 ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
29030 ((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));
29031
29032 if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) {
29033 /* The channel count is supported. */
29034
29035 /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */
29036 ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels);
29037
29038 /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */
29039 ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo);
29040 } else {
29041 /* The channel count is not supported. Skip. */
29042 }
29043 }
29044 }
29045 } else {
29046 /* The format is not supported. Skip. */
29047 }
29048 }
29049
29050 ma_free(pHWParams, &pContext->allocationCallbacks);
29051
29052 ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
29053 return MA_SUCCESS;
29054}
29055
29056static ma_result ma_device_uninit__alsa(ma_device* pDevice)
29057{
29058 MA_ASSERT(pDevice != NULL);
29059
29060 if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) {
29061 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
29062 close(pDevice->alsa.wakeupfdCapture);
29063 ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks);
29064 }
29065
29066 if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) {
29067 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
29068 close(pDevice->alsa.wakeupfdPlayback);
29069 ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks);
29070 }
29071
29072 return MA_SUCCESS;
29073}
29074
29075static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
29076{
29077 ma_result result;
29078 int resultALSA;
29079 ma_snd_pcm_t* pPCM;
29080 ma_bool32 isUsingMMap;
29081 ma_snd_pcm_format_t formatALSA;
29082 ma_format internalFormat;
29083 ma_uint32 internalChannels;
29084 ma_uint32 internalSampleRate;
29085 ma_channel internalChannelMap[MA_MAX_CHANNELS];
29086 ma_uint32 internalPeriodSizeInFrames;
29087 ma_uint32 internalPeriods;
29088 int openMode;
29089 ma_snd_pcm_hw_params_t* pHWParams;
29090 ma_snd_pcm_sw_params_t* pSWParams;
29091 ma_snd_pcm_uframes_t bufferBoundary;
29092 int pollDescriptorCount;
29093 struct pollfd* pPollDescriptors;
29094 int wakeupfd;
29095
29096 MA_ASSERT(pConfig != NULL);
29097 MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */
29098 MA_ASSERT(pDevice != NULL);
29099
29100 formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format);
29101
29102 openMode = 0;
29103 if (pConfig->alsa.noAutoResample) {
29104 openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE;
29105 }
29106 if (pConfig->alsa.noAutoChannels) {
29107 openMode |= MA_SND_PCM_NO_AUTO_CHANNELS;
29108 }
29109 if (pConfig->alsa.noAutoFormat) {
29110 openMode |= MA_SND_PCM_NO_AUTO_FORMAT;
29111 }
29112
29113 result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM);
29114 if (result != MA_SUCCESS) {
29115 return result;
29116 }
29117
29118
29119 /* Hardware parameters. */
29120 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);
29121 if (pHWParams == NULL) {
29122 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29123 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters.");
29124 return MA_OUT_OF_MEMORY;
29125 }
29126
29127 resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
29128 if (resultALSA < 0) {
29129 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
29130 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29131 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.");
29132 return ma_result_from_errno(-resultALSA);
29133 }
29134
29135 /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */
29136 isUsingMMap = MA_FALSE;
29137#if 0 /* NOTE: MMAP mode temporarily disabled. */
29138 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. */
29139 if (!pConfig->alsa.noMMap) {
29140 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) {
29141 pDevice->alsa.isUsingMMap = MA_TRUE;
29142 }
29143 }
29144 }
29145#endif
29146
29147 if (!isUsingMMap) {
29148 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);
29149 if (resultALSA < 0) {
29150 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
29151 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29152 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.");
29153 return ma_result_from_errno(-resultALSA);
29154 }
29155 }
29156
29157 /*
29158 Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't
29159 find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS.
29160 */
29161
29162 /* Format. */
29163 {
29164 /*
29165 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
29166 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.
29167 */
29168 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) {
29169 /* We're either requesting the native format or the specified format is not supported. */
29170 size_t iFormat;
29171
29172 formatALSA = MA_SND_PCM_FORMAT_UNKNOWN;
29173 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) {
29174 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) {
29175 formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]);
29176 break;
29177 }
29178 }
29179
29180 if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) {
29181 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
29182 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29183 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats.");
29185 }
29186 }
29187
29188 resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA);
29189 if (resultALSA < 0) {
29190 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
29191 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29192 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.");
29193 return ma_result_from_errno(-resultALSA);
29194 }
29195
29196 internalFormat = ma_format_from_alsa(formatALSA);
29197 if (internalFormat == ma_format_unknown) {
29198 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
29199 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29200 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio.");
29202 }
29203 }
29204
29205 /* Channels. */
29206 {
29207 unsigned int channels = pDescriptor->channels;
29208 if (channels == 0) {
29209 channels = MA_DEFAULT_CHANNELS;
29210 }
29211
29212 resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels);
29213 if (resultALSA < 0) {
29214 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
29215 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29216 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.");
29217 return ma_result_from_errno(-resultALSA);
29218 }
29219
29220 internalChannels = (ma_uint32)channels;
29221 }
29222
29223 /* Sample Rate */
29224 {
29225 unsigned int sampleRate;
29226
29227 /*
29228 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
29229 problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable
29230 resampling.
29231
29232 To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a
29233 sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling
29234 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
29235 faster rate.
29236
29237 miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine
29238 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
29239 good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion.
29240
29241 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
29242 this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins.
29243 */
29244 ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0);
29245
29246 sampleRate = pDescriptor->sampleRate;
29247 if (sampleRate == 0) {
29248 sampleRate = MA_DEFAULT_SAMPLE_RATE;
29249 }
29250
29251 resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0);
29252 if (resultALSA < 0) {
29253 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
29254 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29255 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.");
29256 return ma_result_from_errno(-resultALSA);
29257 }
29258
29259 internalSampleRate = (ma_uint32)sampleRate;
29260 }
29261
29262 /* Periods. */
29263 {
29264 ma_uint32 periods = pDescriptor->periodCount;
29265
29266 resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL);
29267 if (resultALSA < 0) {
29268 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
29269 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29270 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.");
29271 return ma_result_from_errno(-resultALSA);
29272 }
29273
29274 internalPeriods = periods;
29275 }
29276
29277 /* Buffer Size */
29278 {
29279 ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods;
29280
29281 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);
29282 if (resultALSA < 0) {
29283 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
29284 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29285 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.");
29286 return ma_result_from_errno(-resultALSA);
29287 }
29288
29289 internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods;
29290 }
29291
29292 /* Apply hardware parameters. */
29293 resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams);
29294 if (resultALSA < 0) {
29295 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
29296 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29297 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.");
29298 return ma_result_from_errno(-resultALSA);
29299 }
29300
29301 ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
29302 pHWParams = NULL;
29303
29304
29305 /* Software parameters. */
29306 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);
29307 if (pSWParams == NULL) {
29308 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29309 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters.");
29310 return MA_OUT_OF_MEMORY;
29311 }
29312
29313 resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams);
29314 if (resultALSA < 0) {
29315 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
29316 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29317 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed.");
29318 return ma_result_from_errno(-resultALSA);
29319 }
29320
29321 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));
29322 if (resultALSA < 0) {
29323 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
29324 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29325 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.");
29326 return ma_result_from_errno(-resultALSA);
29327 }
29328
29329 resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary);
29330 if (resultALSA < 0) {
29331 bufferBoundary = internalPeriodSizeInFrames * internalPeriods;
29332 }
29333
29334 if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */
29335 /*
29336 Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to
29337 the size of a period. But for full-duplex we need to set it such that it is at least two periods.
29338 */
29339 resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2);
29340 if (resultALSA < 0) {
29341 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
29342 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29343 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.");
29344 return ma_result_from_errno(-resultALSA);
29345 }
29346
29347 resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary);
29348 if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */
29349 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
29350 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29351 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.");
29352 return ma_result_from_errno(-resultALSA);
29353 }
29354 }
29355
29356 resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams);
29357 if (resultALSA < 0) {
29358 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
29359 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29360 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.");
29361 return ma_result_from_errno(-resultALSA);
29362 }
29363
29364 ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
29365 pSWParams = NULL;
29366
29367
29368 /* 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. */
29369 {
29370 ma_snd_pcm_chmap_t* pChmap = NULL;
29371 if (pDevice->pContext->alsa.snd_pcm_get_chmap != NULL) {
29372 pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM);
29373 }
29374
29375 if (pChmap != NULL) {
29376 ma_uint32 iChannel;
29377
29378 /* 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(). */
29379 if (pChmap->channels >= internalChannels) {
29380 /* Drop excess channels. */
29381 for (iChannel = 0; iChannel < internalChannels; ++iChannel) {
29382 internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
29383 }
29384 } else {
29385 ma_uint32 i;
29386
29387 /*
29388 Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate
29389 channels. If validation fails, fall back to defaults.
29390 */
29391 ma_bool32 isValid = MA_TRUE;
29392
29393 /* Fill with defaults. */
29394 ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
29395
29396 /* Overwrite first pChmap->channels channels. */
29397 for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) {
29398 internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
29399 }
29400
29401 /* Validate. */
29402 for (i = 0; i < internalChannels && isValid; ++i) {
29403 ma_uint32 j;
29404 for (j = i+1; j < internalChannels; ++j) {
29405 if (internalChannelMap[i] == internalChannelMap[j]) {
29406 isValid = MA_FALSE;
29407 break;
29408 }
29409 }
29410 }
29411
29412 /* If our channel map is invalid, fall back to defaults. */
29413 if (!isValid) {
29414 ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
29415 }
29416 }
29417
29418 free(pChmap);
29419 pChmap = NULL;
29420 } else {
29421 /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */
29422 ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
29423 }
29424 }
29425
29426
29427 /*
29428 We need to retrieve the poll descriptors so we can use poll() to wait for data to become
29429 available for reading or writing. There's no well defined maximum for this so we're just going
29430 to allocate this on the heap.
29431 */
29432 pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM);
29433 if (pollDescriptorCount <= 0) {
29434 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29435 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count.");
29436 return MA_ERROR;
29437 }
29438
29439 pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks); /* +1 because we want room for the wakeup descriptor. */
29440 if (pPollDescriptors == NULL) {
29441 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29442 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors.");
29443 return MA_OUT_OF_MEMORY;
29444 }
29445
29446 /*
29447 We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver
29448 never returns from writei() and readi(). This has been observed with the "pulse" device.
29449 */
29450 wakeupfd = eventfd(0, 0);
29451 if (wakeupfd < 0) {
29452 ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
29453 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29454 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup.");
29455 return ma_result_from_errno(errno);
29456 }
29457
29458 /* We'll place the wakeup fd at the start of the buffer. */
29459 pPollDescriptors[0].fd = wakeupfd;
29460 pPollDescriptors[0].events = POLLIN; /* We only care about waiting to read from the wakeup file descriptor. */
29461 pPollDescriptors[0].revents = 0;
29462
29463 /* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */
29464 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. */
29465 if (pollDescriptorCount <= 0) {
29466 close(wakeupfd);
29467 ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
29468 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29469 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors.");
29470 return MA_ERROR;
29471 }
29472
29473 if (deviceType == ma_device_type_capture) {
29474 pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount;
29475 pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors;
29476 pDevice->alsa.wakeupfdCapture = wakeupfd;
29477 } else {
29478 pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount;
29479 pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors;
29480 pDevice->alsa.wakeupfdPlayback = wakeupfd;
29481 }
29482
29483
29484 /* We're done. Prepare the device. */
29485 resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM);
29486 if (resultALSA < 0) {
29487 close(wakeupfd);
29488 ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
29489 ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
29490 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.");
29491 return ma_result_from_errno(-resultALSA);
29492 }
29493
29494
29495 if (deviceType == ma_device_type_capture) {
29496 pDevice->alsa.pPCMCapture = (ma_ptr)pPCM;
29497 pDevice->alsa.isUsingMMapCapture = isUsingMMap;
29498 } else {
29499 pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM;
29500 pDevice->alsa.isUsingMMapPlayback = isUsingMMap;
29501 }
29502
29503 pDescriptor->format = internalFormat;
29504 pDescriptor->channels = internalChannels;
29505 pDescriptor->sampleRate = internalSampleRate;
29506 ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS));
29507 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
29508 pDescriptor->periodCount = internalPeriods;
29509
29510 return MA_SUCCESS;
29511}
29512
29513static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
29514{
29515 MA_ASSERT(pDevice != NULL);
29516
29517 MA_ZERO_OBJECT(&pDevice->alsa);
29518
29519 if (pConfig->deviceType == ma_device_type_loopback) {
29521 }
29522
29523 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
29524 ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
29525 if (result != MA_SUCCESS) {
29526 return result;
29527 }
29528 }
29529
29530 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
29531 ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
29532 if (result != MA_SUCCESS) {
29533 return result;
29534 }
29535 }
29536
29537 return MA_SUCCESS;
29538}
29539
29540static ma_result ma_device_start__alsa(ma_device* pDevice)
29541{
29542 int resultALSA;
29543
29544 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
29545 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
29546 if (resultALSA < 0) {
29547 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device.");
29548 return ma_result_from_errno(-resultALSA);
29549 }
29550 }
29551
29552 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
29553 /*
29554 When data is written to the device we wait for the device to get ready to receive data with poll(). In my testing
29555 I have observed that poll() can sometimes block forever unless the device is started explicitly with snd_pcm_start()
29556 or some data is written with snd_pcm_writei().
29557
29558 To resolve this I've decided to do an explicit start with snd_pcm_start(). The problem with this is that the device
29559 is started without any data in the internal buffer which will result in an immediate underrun. If instead we were
29560 to call into snd_pcm_writei() in an attempt to prevent the underrun, we would run the risk of a weird deadlock
29561 issue as documented inside ma_device_write__alsa().
29562 */
29563 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
29564 if (resultALSA < 0) {
29565 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start playback device.");
29566 return ma_result_from_errno(-resultALSA);
29567 }
29568 }
29569
29570 return MA_SUCCESS;
29571}
29572
29573static ma_result ma_device_stop__alsa(ma_device* pDevice)
29574{
29575 /*
29576 The stop callback will get called on the worker thread after read/write__alsa() has returned. At this point there is
29577 a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable.
29578 */
29579 int resultPoll;
29580 int resultRead;
29581
29582 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
29583 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n");
29584 ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
29585 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device successful.\n");
29586
29587 /* We need to prepare the device again, otherwise we won't be able to restart the device. */
29588 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device...\n");
29589 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
29590 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device failed.\n");
29591 } else {
29592 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n");
29593 }
29594
29595 /* Clear the wakeupfd. */
29596 resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0);
29597 if (resultPoll > 0) {
29598 ma_uint64 t;
29599 resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t));
29600 if (resultRead != sizeof(t)) {
29601 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from capture wakeupfd. read() = %d\n", resultRead);
29602 }
29603 }
29604 }
29605
29606 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
29607 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device...\n");
29608 ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
29609 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device successful.\n");
29610
29611 /* We need to prepare the device again, otherwise we won't be able to restart the device. */
29612 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device...\n");
29613 if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {
29614 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device failed.\n");
29615 } else {
29616 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n");
29617 }
29618
29619 /* Clear the wakeupfd. */
29620 resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0);
29621 if (resultPoll > 0) {
29622 ma_uint64 t;
29623 resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t));
29624 if (resultRead != sizeof(t)) {
29625 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from playback wakeupfd. read() = %d\n", resultRead);
29626 }
29627 }
29628 }
29629
29630 return MA_SUCCESS;
29631}
29632
29633static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent)
29634{
29635 for (;;) {
29636 unsigned short revents;
29637 int resultALSA;
29638 int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1);
29639 if (resultPoll < 0) {
29640 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] poll() failed.\n");
29641
29642 /*
29643 There have been reports that poll() is returning an error randomly and that instead of
29644 returning an error, simply trying again will work. I'm experimenting with adopting this
29645 advice.
29646 */
29647 continue;
29648 /*return ma_result_from_errno(errno);*/
29649 }
29650
29651 /*
29652 Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor
29653 has had it's POLLIN flag set. If so, we need to actually read the data and then exit the
29654 function. The wakeup descriptor will be the first item in the descriptors buffer.
29655 */
29656 if ((pPollDescriptors[0].revents & POLLIN) != 0) {
29657 ma_uint64 t;
29658 int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */
29659 if (resultRead < 0) {
29660 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed.\n");
29661 return ma_result_from_errno(errno);
29662 }
29663
29664 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] POLLIN set for wakeupfd\n");
29665 return MA_DEVICE_NOT_STARTED;
29666 }
29667
29668 /*
29669 Getting here means that some data should be able to be read. We need to use ALSA to
29670 translate the revents flags for us.
29671 */
29672 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. */
29673 if (resultALSA < 0) {
29674 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.\n");
29675 return ma_result_from_errno(-resultALSA);
29676 }
29677
29678 if ((revents & POLLERR) != 0) {
29679 ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM);
29680 if (state == MA_SND_PCM_STATE_XRUN) {
29681 /* The PCM is in a xrun state. This will be recovered from at a higher level. We can disregard this. */
29682 } else {
29683 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] POLLERR detected. status = %d\n", ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM));
29684 }
29685 }
29686
29687 if ((revents & requiredEvent) == requiredEvent) {
29688 break; /* We're done. Data available for reading or writing. */
29689 }
29690 }
29691
29692 return MA_SUCCESS;
29693}
29694
29695static ma_result ma_device_wait_read__alsa(ma_device* pDevice)
29696{
29697 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. */
29698}
29699
29700static ma_result ma_device_wait_write__alsa(ma_device* pDevice)
29701{
29702 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. */
29703}
29704
29705static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead)
29706{
29707 ma_snd_pcm_sframes_t resultALSA = 0;
29708
29709 MA_ASSERT(pDevice != NULL);
29710 MA_ASSERT(pFramesOut != NULL);
29711
29712 if (pFramesRead != NULL) {
29713 *pFramesRead = 0;
29714 }
29715
29716 while (ma_device_get_state(pDevice) == ma_device_state_started) {
29717 ma_result result;
29718
29719 /* 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. */
29720 result = ma_device_wait_read__alsa(pDevice);
29721 if (result != MA_SUCCESS) {
29722 return result;
29723 }
29724
29725 /* Getting here means we should have data available. */
29726 resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
29727 if (resultALSA >= 0) {
29728 break; /* Success. */
29729 } else {
29730 if (resultALSA == -EAGAIN) {
29731 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (read)\n");*/
29732 continue; /* Try again. */
29733 } else if (resultALSA == -EPIPE) {
29734 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (read)\n");
29735
29736 /* Overrun. Recover and try again. If this fails we need to return an error. */
29737 resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE);
29738 if (resultALSA < 0) {
29739 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.");
29740 return ma_result_from_errno((int)-resultALSA);
29741 }
29742
29743 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
29744 if (resultALSA < 0) {
29745 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.");
29746 return ma_result_from_errno((int)-resultALSA);
29747 }
29748
29749 continue; /* Try reading again. */
29750 }
29751 }
29752 }
29753
29754 if (pFramesRead != NULL) {
29755 *pFramesRead = resultALSA;
29756 }
29757
29758 return MA_SUCCESS;
29759}
29760
29761static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
29762{
29763 ma_snd_pcm_sframes_t resultALSA = 0;
29764
29765 MA_ASSERT(pDevice != NULL);
29766 MA_ASSERT(pFrames != NULL);
29767
29768 if (pFramesWritten != NULL) {
29769 *pFramesWritten = 0;
29770 }
29771
29772 while (ma_device_get_state(pDevice) == ma_device_state_started) {
29773 ma_result result;
29774
29775 /* 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. */
29776 result = ma_device_wait_write__alsa(pDevice);
29777 if (result != MA_SUCCESS) {
29778 return result;
29779 }
29780
29781 resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
29782 if (resultALSA >= 0) {
29783 break; /* Success. */
29784 } else {
29785 if (resultALSA == -EAGAIN) {
29786 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (write)\n");*/
29787 continue; /* Try again. */
29788 } else if (resultALSA == -EPIPE) {
29789 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (write)\n");
29790
29791 /* Underrun. Recover and try again. If this fails we need to return an error. */
29792 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). */
29793 if (resultALSA < 0) {
29794 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.");
29795 return ma_result_from_errno((int)-resultALSA);
29796 }
29797
29798 /*
29799 In my testing I have had a situation where writei() does not automatically restart the device even though I've set it
29800 up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of
29801 frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure
29802 if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't
29803 quite right here.
29804 */
29805 resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
29806 if (resultALSA < 0) {
29807 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.");
29808 return ma_result_from_errno((int)-resultALSA);
29809 }
29810
29811 continue; /* Try writing again. */
29812 }
29813 }
29814 }
29815
29816 if (pFramesWritten != NULL) {
29817 *pFramesWritten = resultALSA;
29818 }
29819
29820 return MA_SUCCESS;
29821}
29822
29823static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice)
29824{
29825 ma_uint64 t = 1;
29826 int resultWrite = 0;
29827
29828 MA_ASSERT(pDevice != NULL);
29829
29830 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up...\n");
29831
29832 /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */
29833 if (pDevice->alsa.pPollDescriptorsCapture != NULL) {
29834 resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t));
29835 }
29836 if (pDevice->alsa.pPollDescriptorsPlayback != NULL) {
29837 resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t));
29838 }
29839
29840 if (resultWrite < 0) {
29841 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] write() failed.\n");
29842 return ma_result_from_errno(errno);
29843 }
29844
29845 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up completed successfully.\n");
29846
29847 return MA_SUCCESS;
29848}
29849
29850static ma_result ma_context_uninit__alsa(ma_context* pContext)
29851{
29852 MA_ASSERT(pContext != NULL);
29853 MA_ASSERT(pContext->backend == ma_backend_alsa);
29854
29855 /* Clean up memory for memory leak checkers. */
29856 ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)();
29857
29858#ifndef MA_NO_RUNTIME_LINKING
29859 ma_dlclose(ma_context_get_log(pContext), pContext->alsa.asoundSO);
29860#endif
29861
29862 ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock);
29863
29864 return MA_SUCCESS;
29865}
29866
29867static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
29868{
29869 ma_result result;
29870#ifndef MA_NO_RUNTIME_LINKING
29871 const char* libasoundNames[] = {
29872 "libasound.so.2",
29873 "libasound.so"
29874 };
29875 size_t i;
29876
29877 for (i = 0; i < ma_countof(libasoundNames); ++i) {
29878 pContext->alsa.asoundSO = ma_dlopen(ma_context_get_log(pContext), libasoundNames[i]);
29879 if (pContext->alsa.asoundSO != NULL) {
29880 break;
29881 }
29882 }
29883
29884 if (pContext->alsa.asoundSO == NULL) {
29885 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n");
29886 return MA_NO_BACKEND;
29887 }
29888
29889 pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_open");
29890 pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_close");
29891 pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof");
29892 pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_any");
29893 pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format");
29894 pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first");
29895 pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask");
29896 pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels");
29897 pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near");
29898 pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax");
29899 pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample");
29900 pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate");
29901 pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near");
29902 pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near");
29903 pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near");
29904 pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access");
29905 pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format");
29906 pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels");
29907 pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min");
29908 pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max");
29909 pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate");
29910 pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min");
29911 pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max");
29912 pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size");
29913 pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods");
29914 pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access");
29915 pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format");
29916 pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels");
29917 pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate");
29918 pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params");
29919 pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof");
29920 pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_current");
29921 pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary");
29922 pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min");
29923 pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold");
29924 pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold");
29925 pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params");
29926 pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof");
29927 pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_test");
29928 pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_get_chmap");
29929 pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_state");
29930 pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_prepare");
29931 pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_start");
29932 pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drop");
29933 pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drain");
29934 pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_reset");
29935 pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_hint");
29936 pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_get_hint");
29937 pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_card_get_index");
29938 pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_free_hint");
29939 pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_begin");
29940 pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_commit");
29941 pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_recover");
29942 pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_readi");
29943 pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_writei");
29944 pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail");
29945 pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail_update");
29946 pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_wait");
29947 pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_nonblock");
29948 pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info");
29949 pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_sizeof");
29950 pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_get_name");
29951 pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors");
29952 pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count");
29953 pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents");
29954 pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_config_update_free_global");
29955#else
29956 /* The system below is just for type safety. */
29957 ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open;
29958 ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close;
29959 ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof;
29960 ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any;
29961 ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format;
29962 ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first;
29963 ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask;
29964 ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels;
29965 ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near;
29966 ma_snd_pcm_hw_params_set_channels_minmax_proc _snd_pcm_hw_params_set_channels_minmax = snd_pcm_hw_params_set_channels_minmax;
29967 ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample;
29968 ma_snd_pcm_hw_params_set_rate_proc _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate;
29969 ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near;
29970 ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax;
29971 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;
29972 ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near;
29973 ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access;
29974 ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format;
29975 ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels;
29976 ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min;
29977 ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max;
29978 ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate;
29979 ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min;
29980 ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max;
29981 ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size;
29982 ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods;
29983 ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access;
29984 ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format;
29985 ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels;
29986 ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate;
29987 ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params;
29988 ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof;
29989 ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current;
29990 ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary;
29991 ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min;
29992 ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold;
29993 ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold;
29994 ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params;
29995 ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof;
29996 ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test;
29997 ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap;
29998 ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state;
29999 ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare;
30000 ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start;
30001 ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop;
30002 ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain;
30003 ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset;
30004 ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint;
30005 ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint;
30006 ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index;
30007 ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint;
30008 ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin;
30009 ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit;
30010 ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover;
30011 ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi;
30012 ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei;
30013 ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail;
30014 ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update;
30015 ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait;
30016 ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock;
30017 ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info;
30018 ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof;
30019 ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name;
30020 ma_snd_pcm_poll_descriptors_proc _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors;
30021 ma_snd_pcm_poll_descriptors_count_proc _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count;
30022 ma_snd_pcm_poll_descriptors_revents_proc _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents;
30023 ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global;
30024
30025 pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open;
30026 pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close;
30027 pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof;
30028 pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any;
30029 pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format;
30030 pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first;
30031 pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask;
30032 pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels;
30033 pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near;
30034 pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax;
30035 pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample;
30036 pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate;
30037 pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near;
30038 pContext->alsa.snd_pcm_hw_params_set_rate_minmax = (ma_proc)_snd_pcm_hw_params_set_rate_minmax;
30039 pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near;
30040 pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near;
30041 pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access;
30042 pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format;
30043 pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels;
30044 pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min;
30045 pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max;
30046 pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate;
30047 pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min;
30048 pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max;
30049 pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size;
30050 pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods;
30051 pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access;
30052 pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format;
30053 pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels;
30054 pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate;
30055 pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params;
30056 pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof;
30057 pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current;
30058 pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary;
30059 pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min;
30060 pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold;
30061 pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold;
30062 pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params;
30063 pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof;
30064 pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test;
30065 pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap;
30066 pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state;
30067 pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare;
30068 pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start;
30069 pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop;
30070 pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain;
30071 pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset;
30072 pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint;
30073 pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint;
30074 pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index;
30075 pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint;
30076 pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin;
30077 pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit;
30078 pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover;
30079 pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi;
30080 pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei;
30081 pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail;
30082 pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update;
30083 pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait;
30084 pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock;
30085 pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info;
30086 pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof;
30087 pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name;
30088 pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors;
30089 pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count;
30090 pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents;
30091 pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global;
30092#endif
30093
30094 pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration;
30095
30096 result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock);
30097 if (result != MA_SUCCESS) {
30098 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration.");
30099 return result;
30100 }
30101
30102 pCallbacks->onContextInit = ma_context_init__alsa;
30103 pCallbacks->onContextUninit = ma_context_uninit__alsa;
30104 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa;
30105 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa;
30106 pCallbacks->onDeviceInit = ma_device_init__alsa;
30107 pCallbacks->onDeviceUninit = ma_device_uninit__alsa;
30108 pCallbacks->onDeviceStart = ma_device_start__alsa;
30109 pCallbacks->onDeviceStop = ma_device_stop__alsa;
30110 pCallbacks->onDeviceRead = ma_device_read__alsa;
30111 pCallbacks->onDeviceWrite = ma_device_write__alsa;
30112 pCallbacks->onDeviceDataLoop = NULL;
30113 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa;
30114
30115 return MA_SUCCESS;
30116}
30117#endif /* MA_HAS_ALSA */
30118
30119
30120
30121/******************************************************************************
30122
30123PulseAudio Backend
30124
30125******************************************************************************/
30126#ifdef MA_HAS_PULSEAUDIO
30127/*
30128The PulseAudio API, along with Apple's Core Audio, is the worst of the mainstream audio APIs. This is a brief description of what's going on
30129in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion.
30130
30131PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it
30132allows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it
30133appears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or
30134write functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the
30135simple API has some smarts to do this automatically, but I'm not sure. Another limitation with the simple API is that it seems inefficient
30136when you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API.
30137
30138Since 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
30139get fun, and I don't mean that in a good way...
30140
30141The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands
30142don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is
30143enabled through the use of a "main loop". In the asynchronous API you cannot get away from the main loop, and the main loop is where almost
30144all of PulseAudio's problems stem from.
30145
30146When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own
30147vtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called
30148pa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop
30149because it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use
30150it to implement a worker thread which runs in a loop. The main loop is where operations are actually executed.
30151
30152To 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
30153to the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded
30154main loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely
30155specialized such as if you want to integrate it into your application's existing main loop infrastructure.
30156
30157(EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262.
30158It is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.)
30159
30160Once you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to
30161miniaudio'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
30162one `ma_context` to many `ma_device`s. Here's where it starts to get annoying, however. When you first create the PulseAudio context, which
30163is done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if
30164you remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()`
30165has 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
30166set 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.
30167All objects from here out are created from the context, and, I believe, you can't be creating these objects until the context is connected.
30168This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before
30169attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`.
30170
30171The reason for this asynchronous design is to support cases where you're connecting to a remote server, say through a local network or an
30172internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the
30173host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind.
30174
30175Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device.
30176The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call
30177`pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get
30178information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object
30179is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to
30180run 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
30181context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up.
30182All of that just to retrieve basic information about a device!
30183
30184Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the
30185context, 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
30186choices in PulseAudio.
30187
30188PulseAudio 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
30189because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for
30190writing 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
30191set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices
30192straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified,
30193PulseAudio will immediately fire its write or read callback. This is *technically* correct (based on the wording in the documentation)
30194because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback
30195would 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
30196requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at
30197that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the
30198stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data
30199callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio
30200doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been
30201started. 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
30202callback is not fired.
30203
30204This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will
30205continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device
30206is running, and will be fired regardless of whether or not the user actually wrote anything to the device/stream. This not the case in
30207PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call
30208`pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always
30209writing 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
30210you're continuously writing data to the stream, the stream will never get drained! That means in order to drain the stream, you need to
30211*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
30212important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained
30213before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write
30214data 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!
30215
30216This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not*
30217write 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
30218resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This
30219disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the
30220callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.)
30221
30222Once you've created the stream, you need to connect it which involves the whole waiting procedure. This is the same process as the context,
30223only this time you'll poll for the state with `pa_stream_get_status()`. The starting and stopping of a streaming is referred to as
30224"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
30225it'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
30226guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is
30227absolutely beyond me. Would it really be that hard to just make it run synchronously?
30228
30229Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that
30230they were initialized in.
30231
30232That'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
30233embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to
30234run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche
30235requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is
30236constantly fired, regardless of whether or not `pa_stream_write()` has been called, and it needs to take a pointer to a buffer as a
30237parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These
30238changes alone will change PulseAudio from one of the worst audio APIs to one of the best.
30239*/
30240
30241
30242/*
30243It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header
30244to check for type safety. We cannot do this when linking at run time because the header might not be available.
30245*/
30246#ifdef MA_NO_RUNTIME_LINKING
30247
30248/* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */
30249#if !defined(__cplusplus)
30250 #if defined(__STRICT_ANSI__)
30251 #if !defined(inline)
30252 #define inline __inline__ __attribute__((always_inline))
30253 #define MA_INLINE_DEFINED
30254 #endif
30255 #endif
30256#endif
30257#include <pulse/pulseaudio.h>
30258#if defined(MA_INLINE_DEFINED)
30259 #undef inline
30260 #undef MA_INLINE_DEFINED
30261#endif
30262
30263#define MA_PA_OK PA_OK
30264#define MA_PA_ERR_ACCESS PA_ERR_ACCESS
30265#define MA_PA_ERR_INVALID PA_ERR_INVALID
30266#define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY
30267#define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED
30268
30269#define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX
30270#define MA_PA_RATE_MAX PA_RATE_MAX
30271
30272typedef pa_context_flags_t ma_pa_context_flags_t;
30273#define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS
30274#define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN
30275#define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL
30276
30277typedef pa_stream_flags_t ma_pa_stream_flags_t;
30278#define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS
30279#define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED
30280#define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING
30281#define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC
30282#define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE
30283#define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS
30284#define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS
30285#define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT
30286#define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE
30287#define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS
30288#define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE
30289#define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE
30290#define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT
30291#define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED
30292#define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY
30293#define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS
30294#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
30295#define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
30296#define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
30297#define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
30298#define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
30299
30300typedef pa_sink_flags_t ma_pa_sink_flags_t;
30301#define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS
30302#define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL
30303#define MA_PA_SINK_LATENCY PA_SINK_LATENCY
30304#define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE
30305#define MA_PA_SINK_NETWORK PA_SINK_NETWORK
30306#define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL
30307#define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
30308#define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
30309#define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY
30310#define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS
30311
30312typedef pa_source_flags_t ma_pa_source_flags_t;
30313#define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS
30314#define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL
30315#define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY
30316#define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE
30317#define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK
30318#define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL
30319#define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME
30320#define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY
30321#define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME
30322
30323typedef pa_context_state_t ma_pa_context_state_t;
30324#define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED
30325#define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING
30326#define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING
30327#define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME
30328#define MA_PA_CONTEXT_READY PA_CONTEXT_READY
30329#define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED
30330#define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED
30331
30332typedef pa_stream_state_t ma_pa_stream_state_t;
30333#define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED
30334#define MA_PA_STREAM_CREATING PA_STREAM_CREATING
30335#define MA_PA_STREAM_READY PA_STREAM_READY
30336#define MA_PA_STREAM_FAILED PA_STREAM_FAILED
30337#define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED
30338
30339typedef pa_operation_state_t ma_pa_operation_state_t;
30340#define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING
30341#define MA_PA_OPERATION_DONE PA_OPERATION_DONE
30342#define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED
30343
30344typedef pa_sink_state_t ma_pa_sink_state_t;
30345#define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE
30346#define MA_PA_SINK_RUNNING PA_SINK_RUNNING
30347#define MA_PA_SINK_IDLE PA_SINK_IDLE
30348#define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED
30349
30350typedef pa_source_state_t ma_pa_source_state_t;
30351#define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE
30352#define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING
30353#define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE
30354#define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED
30355
30356typedef pa_seek_mode_t ma_pa_seek_mode_t;
30357#define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE
30358#define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE
30359#define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ
30360#define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END
30361
30362typedef pa_channel_position_t ma_pa_channel_position_t;
30363#define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID
30364#define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO
30365#define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT
30366#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT
30367#define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER
30368#define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER
30369#define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT
30370#define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT
30371#define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE
30372#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
30373#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
30374#define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT
30375#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT
30376#define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0
30377#define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1
30378#define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2
30379#define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3
30380#define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4
30381#define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5
30382#define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6
30383#define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7
30384#define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8
30385#define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9
30386#define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10
30387#define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11
30388#define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12
30389#define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13
30390#define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14
30391#define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15
30392#define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16
30393#define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17
30394#define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18
30395#define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19
30396#define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20
30397#define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21
30398#define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22
30399#define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23
30400#define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24
30401#define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25
30402#define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26
30403#define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27
30404#define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28
30405#define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29
30406#define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30
30407#define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31
30408#define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER
30409#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT
30410#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
30411#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER
30412#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT
30413#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT
30414#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER
30415#define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT
30416#define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT
30417#define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER
30418#define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER
30419
30420typedef pa_channel_map_def_t ma_pa_channel_map_def_t;
30421#define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF
30422#define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA
30423#define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX
30424#define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX
30425#define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS
30426#define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT
30427
30428typedef pa_sample_format_t ma_pa_sample_format_t;
30429#define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID
30430#define MA_PA_SAMPLE_U8 PA_SAMPLE_U8
30431#define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW
30432#define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW
30433#define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE
30434#define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE
30435#define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE
30436#define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE
30437#define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE
30438#define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE
30439#define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE
30440#define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE
30441#define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE
30442#define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE
30443
30444typedef pa_mainloop ma_pa_mainloop;
30445typedef pa_threaded_mainloop ma_pa_threaded_mainloop;
30446typedef pa_mainloop_api ma_pa_mainloop_api;
30447typedef pa_context ma_pa_context;
30448typedef pa_operation ma_pa_operation;
30449typedef pa_stream ma_pa_stream;
30450typedef pa_spawn_api ma_pa_spawn_api;
30451typedef pa_buffer_attr ma_pa_buffer_attr;
30452typedef pa_channel_map ma_pa_channel_map;
30453typedef pa_cvolume ma_pa_cvolume;
30454typedef pa_sample_spec ma_pa_sample_spec;
30455typedef pa_sink_info ma_pa_sink_info;
30456typedef pa_source_info ma_pa_source_info;
30457
30458typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t;
30459typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t;
30460typedef pa_source_info_cb_t ma_pa_source_info_cb_t;
30461typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t;
30462typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t;
30463typedef pa_stream_notify_cb_t ma_pa_stream_notify_cb_t;
30464typedef pa_free_cb_t ma_pa_free_cb_t;
30465#else
30466#define MA_PA_OK 0
30467#define MA_PA_ERR_ACCESS 1
30468#define MA_PA_ERR_INVALID 2
30469#define MA_PA_ERR_NOENTITY 5
30470#define MA_PA_ERR_NOTSUPPORTED 19
30471
30472#define MA_PA_CHANNELS_MAX 32
30473#define MA_PA_RATE_MAX 384000
30474
30475typedef int ma_pa_context_flags_t;
30476#define MA_PA_CONTEXT_NOFLAGS 0x00000000
30477#define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001
30478#define MA_PA_CONTEXT_NOFAIL 0x00000002
30479
30480typedef int ma_pa_stream_flags_t;
30481#define MA_PA_STREAM_NOFLAGS 0x00000000
30482#define MA_PA_STREAM_START_CORKED 0x00000001
30483#define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002
30484#define MA_PA_STREAM_NOT_MONOTONIC 0x00000004
30485#define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008
30486#define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010
30487#define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020
30488#define MA_PA_STREAM_FIX_FORMAT 0x00000040
30489#define MA_PA_STREAM_FIX_RATE 0x00000080
30490#define MA_PA_STREAM_FIX_CHANNELS 0x00000100
30491#define MA_PA_STREAM_DONT_MOVE 0x00000200
30492#define MA_PA_STREAM_VARIABLE_RATE 0x00000400
30493#define MA_PA_STREAM_PEAK_DETECT 0x00000800
30494#define MA_PA_STREAM_START_MUTED 0x00001000
30495#define MA_PA_STREAM_ADJUST_LATENCY 0x00002000
30496#define MA_PA_STREAM_EARLY_REQUESTS 0x00004000
30497#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000
30498#define MA_PA_STREAM_START_UNMUTED 0x00010000
30499#define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000
30500#define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000
30501#define MA_PA_STREAM_PASSTHROUGH 0x00080000
30502
30503typedef int ma_pa_sink_flags_t;
30504#define MA_PA_SINK_NOFLAGS 0x00000000
30505#define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001
30506#define MA_PA_SINK_LATENCY 0x00000002
30507#define MA_PA_SINK_HARDWARE 0x00000004
30508#define MA_PA_SINK_NETWORK 0x00000008
30509#define MA_PA_SINK_HW_MUTE_CTRL 0x00000010
30510#define MA_PA_SINK_DECIBEL_VOLUME 0x00000020
30511#define MA_PA_SINK_FLAT_VOLUME 0x00000040
30512#define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080
30513#define MA_PA_SINK_SET_FORMATS 0x00000100
30514
30515typedef int ma_pa_source_flags_t;
30516#define MA_PA_SOURCE_NOFLAGS 0x00000000
30517#define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001
30518#define MA_PA_SOURCE_LATENCY 0x00000002
30519#define MA_PA_SOURCE_HARDWARE 0x00000004
30520#define MA_PA_SOURCE_NETWORK 0x00000008
30521#define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010
30522#define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020
30523#define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040
30524#define MA_PA_SOURCE_FLAT_VOLUME 0x00000080
30525
30526typedef int ma_pa_context_state_t;
30527#define MA_PA_CONTEXT_UNCONNECTED 0
30528#define MA_PA_CONTEXT_CONNECTING 1
30529#define MA_PA_CONTEXT_AUTHORIZING 2
30530#define MA_PA_CONTEXT_SETTING_NAME 3
30531#define MA_PA_CONTEXT_READY 4
30532#define MA_PA_CONTEXT_FAILED 5
30533#define MA_PA_CONTEXT_TERMINATED 6
30534
30535typedef int ma_pa_stream_state_t;
30536#define MA_PA_STREAM_UNCONNECTED 0
30537#define MA_PA_STREAM_CREATING 1
30538#define MA_PA_STREAM_READY 2
30539#define MA_PA_STREAM_FAILED 3
30540#define MA_PA_STREAM_TERMINATED 4
30541
30542typedef int ma_pa_operation_state_t;
30543#define MA_PA_OPERATION_RUNNING 0
30544#define MA_PA_OPERATION_DONE 1
30545#define MA_PA_OPERATION_CANCELLED 2
30546
30547typedef int ma_pa_sink_state_t;
30548#define MA_PA_SINK_INVALID_STATE -1
30549#define MA_PA_SINK_RUNNING 0
30550#define MA_PA_SINK_IDLE 1
30551#define MA_PA_SINK_SUSPENDED 2
30552
30553typedef int ma_pa_source_state_t;
30554#define MA_PA_SOURCE_INVALID_STATE -1
30555#define MA_PA_SOURCE_RUNNING 0
30556#define MA_PA_SOURCE_IDLE 1
30557#define MA_PA_SOURCE_SUSPENDED 2
30558
30559typedef int ma_pa_seek_mode_t;
30560#define MA_PA_SEEK_RELATIVE 0
30561#define MA_PA_SEEK_ABSOLUTE 1
30562#define MA_PA_SEEK_RELATIVE_ON_READ 2
30563#define MA_PA_SEEK_RELATIVE_END 3
30564
30565typedef int ma_pa_channel_position_t;
30566#define MA_PA_CHANNEL_POSITION_INVALID -1
30567#define MA_PA_CHANNEL_POSITION_MONO 0
30568#define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1
30569#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2
30570#define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3
30571#define MA_PA_CHANNEL_POSITION_REAR_CENTER 4
30572#define MA_PA_CHANNEL_POSITION_REAR_LEFT 5
30573#define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6
30574#define MA_PA_CHANNEL_POSITION_LFE 7
30575#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8
30576#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9
30577#define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10
30578#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11
30579#define MA_PA_CHANNEL_POSITION_AUX0 12
30580#define MA_PA_CHANNEL_POSITION_AUX1 13
30581#define MA_PA_CHANNEL_POSITION_AUX2 14
30582#define MA_PA_CHANNEL_POSITION_AUX3 15
30583#define MA_PA_CHANNEL_POSITION_AUX4 16
30584#define MA_PA_CHANNEL_POSITION_AUX5 17
30585#define MA_PA_CHANNEL_POSITION_AUX6 18
30586#define MA_PA_CHANNEL_POSITION_AUX7 19
30587#define MA_PA_CHANNEL_POSITION_AUX8 20
30588#define MA_PA_CHANNEL_POSITION_AUX9 21
30589#define MA_PA_CHANNEL_POSITION_AUX10 22
30590#define MA_PA_CHANNEL_POSITION_AUX11 23
30591#define MA_PA_CHANNEL_POSITION_AUX12 24
30592#define MA_PA_CHANNEL_POSITION_AUX13 25
30593#define MA_PA_CHANNEL_POSITION_AUX14 26
30594#define MA_PA_CHANNEL_POSITION_AUX15 27
30595#define MA_PA_CHANNEL_POSITION_AUX16 28
30596#define MA_PA_CHANNEL_POSITION_AUX17 29
30597#define MA_PA_CHANNEL_POSITION_AUX18 30
30598#define MA_PA_CHANNEL_POSITION_AUX19 31
30599#define MA_PA_CHANNEL_POSITION_AUX20 32
30600#define MA_PA_CHANNEL_POSITION_AUX21 33
30601#define MA_PA_CHANNEL_POSITION_AUX22 34
30602#define MA_PA_CHANNEL_POSITION_AUX23 35
30603#define MA_PA_CHANNEL_POSITION_AUX24 36
30604#define MA_PA_CHANNEL_POSITION_AUX25 37
30605#define MA_PA_CHANNEL_POSITION_AUX26 38
30606#define MA_PA_CHANNEL_POSITION_AUX27 39
30607#define MA_PA_CHANNEL_POSITION_AUX28 40
30608#define MA_PA_CHANNEL_POSITION_AUX29 41
30609#define MA_PA_CHANNEL_POSITION_AUX30 42
30610#define MA_PA_CHANNEL_POSITION_AUX31 43
30611#define MA_PA_CHANNEL_POSITION_TOP_CENTER 44
30612#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45
30613#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46
30614#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47
30615#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48
30616#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49
30617#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50
30618#define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT
30619#define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT
30620#define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER
30621#define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE
30622
30623typedef int ma_pa_channel_map_def_t;
30624#define MA_PA_CHANNEL_MAP_AIFF 0
30625#define MA_PA_CHANNEL_MAP_ALSA 1
30626#define MA_PA_CHANNEL_MAP_AUX 2
30627#define MA_PA_CHANNEL_MAP_WAVEEX 3
30628#define MA_PA_CHANNEL_MAP_OSS 4
30629#define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF
30630
30631typedef int ma_pa_sample_format_t;
30632#define MA_PA_SAMPLE_INVALID -1
30633#define MA_PA_SAMPLE_U8 0
30634#define MA_PA_SAMPLE_ALAW 1
30635#define MA_PA_SAMPLE_ULAW 2
30636#define MA_PA_SAMPLE_S16LE 3
30637#define MA_PA_SAMPLE_S16BE 4
30638#define MA_PA_SAMPLE_FLOAT32LE 5
30639#define MA_PA_SAMPLE_FLOAT32BE 6
30640#define MA_PA_SAMPLE_S32LE 7
30641#define MA_PA_SAMPLE_S32BE 8
30642#define MA_PA_SAMPLE_S24LE 9
30643#define MA_PA_SAMPLE_S24BE 10
30644#define MA_PA_SAMPLE_S24_32LE 11
30645#define MA_PA_SAMPLE_S24_32BE 12
30646
30647typedef struct ma_pa_mainloop ma_pa_mainloop;
30648typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop;
30649typedef struct ma_pa_mainloop_api ma_pa_mainloop_api;
30650typedef struct ma_pa_context ma_pa_context;
30651typedef struct ma_pa_operation ma_pa_operation;
30652typedef struct ma_pa_stream ma_pa_stream;
30653typedef struct ma_pa_spawn_api ma_pa_spawn_api;
30654
30655typedef struct
30656{
30657 ma_uint32 maxlength;
30658 ma_uint32 tlength;
30659 ma_uint32 prebuf;
30660 ma_uint32 minreq;
30661 ma_uint32 fragsize;
30662} ma_pa_buffer_attr;
30663
30664typedef struct
30665{
30666 ma_uint8 channels;
30667 ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX];
30668} ma_pa_channel_map;
30669
30670typedef struct
30671{
30672 ma_uint8 channels;
30673 ma_uint32 values[MA_PA_CHANNELS_MAX];
30674} ma_pa_cvolume;
30675
30676typedef struct
30677{
30678 ma_pa_sample_format_t format;
30680 ma_uint8 channels;
30681} ma_pa_sample_spec;
30682
30683typedef struct
30684{
30685 const char* name;
30686 ma_uint32 index;
30687 const char* description;
30688 ma_pa_sample_spec sample_spec;
30689 ma_pa_channel_map channel_map;
30690 ma_uint32 owner_module;
30691 ma_pa_cvolume volume;
30692 int mute;
30693 ma_uint32 monitor_source;
30694 const char* monitor_source_name;
30695 ma_uint64 latency;
30696 const char* driver;
30697 ma_pa_sink_flags_t flags;
30698 void* proplist;
30699 ma_uint64 configured_latency;
30700 ma_uint32 base_volume;
30701 ma_pa_sink_state_t state;
30702 ma_uint32 n_volume_steps;
30703 ma_uint32 card;
30704 ma_uint32 n_ports;
30705 void** ports;
30706 void* active_port;
30707 ma_uint8 n_formats;
30708 void** formats;
30709} ma_pa_sink_info;
30710
30711typedef struct
30712{
30713 const char *name;
30714 ma_uint32 index;
30715 const char *description;
30716 ma_pa_sample_spec sample_spec;
30717 ma_pa_channel_map channel_map;
30718 ma_uint32 owner_module;
30719 ma_pa_cvolume volume;
30720 int mute;
30721 ma_uint32 monitor_of_sink;
30722 const char *monitor_of_sink_name;
30723 ma_uint64 latency;
30724 const char *driver;
30725 ma_pa_source_flags_t flags;
30726 void* proplist;
30727 ma_uint64 configured_latency;
30728 ma_uint32 base_volume;
30729 ma_pa_source_state_t state;
30730 ma_uint32 n_volume_steps;
30731 ma_uint32 card;
30732 ma_uint32 n_ports;
30733 void** ports;
30734 void* active_port;
30735 ma_uint8 n_formats;
30736 void** formats;
30737} ma_pa_source_info;
30738
30739typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata);
30740typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata);
30741typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata);
30742typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata);
30743typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata);
30744typedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata);
30745typedef void (* ma_pa_free_cb_t) (void* p);
30746#endif
30747
30748
30749typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void);
30750typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m);
30751typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval);
30752typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m);
30753typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval);
30754typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m);
30755typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void);
30756typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m);
30757typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m);
30758typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m);
30759typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m);
30760typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m);
30761typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m);
30762typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept);
30763typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m);
30764typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (const ma_pa_threaded_mainloop* m);
30765typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m);
30766typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m);
30767typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name);
30768typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name);
30769typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c);
30770typedef 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);
30771typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c);
30772typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata);
30773typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (const ma_pa_context* c);
30774typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata);
30775typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata);
30776typedef 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);
30777typedef 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);
30778typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o);
30779typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (const ma_pa_operation* o);
30780typedef 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);
30781typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m);
30782typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss);
30783typedef 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);
30784typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s);
30785typedef 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);
30786typedef 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);
30787typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s);
30788typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (const ma_pa_stream* s);
30789typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s);
30790typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s);
30791typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s);
30792typedef 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);
30793typedef const char* (* ma_pa_stream_get_device_name_proc) (const ma_pa_stream* s);
30794typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
30795typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
30796typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);
30797typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);
30798typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s);
30799typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
30800typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
30801typedef int (* ma_pa_stream_is_corked_proc) (const ma_pa_stream* s);
30802typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata);
30803typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
30804typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes);
30805typedef 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);
30806typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes);
30807typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s);
30808typedef size_t (* ma_pa_stream_writable_size_proc) (const ma_pa_stream* s);
30809typedef size_t (* ma_pa_stream_readable_size_proc) (const ma_pa_stream* s);
30810
30811typedef struct
30812{
30813 ma_uint32 count;
30814 ma_uint32 capacity;
30815 ma_device_info* pInfo;
30816} ma_pulse_device_enum_data;
30817
30818static ma_result ma_result_from_pulse(int result)
30819{
30820 if (result < 0) {
30821 return MA_ERROR;
30822 }
30823
30824 switch (result) {
30825 case MA_PA_OK: return MA_SUCCESS;
30826 case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED;
30827 case MA_PA_ERR_INVALID: return MA_INVALID_ARGS;
30828 case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE;
30829 default: return MA_ERROR;
30830 }
30831}
30832
30833#if 0
30834static ma_pa_sample_format_t ma_format_to_pulse(ma_format format)
30835{
30836 if (ma_is_little_endian()) {
30837 switch (format) {
30838 case ma_format_s16: return MA_PA_SAMPLE_S16LE;
30839 case ma_format_s24: return MA_PA_SAMPLE_S24LE;
30840 case ma_format_s32: return MA_PA_SAMPLE_S32LE;
30841 case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE;
30842 default: break;
30843 }
30844 } else {
30845 switch (format) {
30846 case ma_format_s16: return MA_PA_SAMPLE_S16BE;
30847 case ma_format_s24: return MA_PA_SAMPLE_S24BE;
30848 case ma_format_s32: return MA_PA_SAMPLE_S32BE;
30849 case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE;
30850 default: break;
30851 }
30852 }
30853
30854 /* Endian agnostic. */
30855 switch (format) {
30856 case ma_format_u8: return MA_PA_SAMPLE_U8;
30857 default: return MA_PA_SAMPLE_INVALID;
30858 }
30859}
30860#endif
30861
30862static ma_format ma_format_from_pulse(ma_pa_sample_format_t format)
30863{
30864 if (ma_is_little_endian()) {
30865 switch (format) {
30866 case MA_PA_SAMPLE_S16LE: return ma_format_s16;
30867 case MA_PA_SAMPLE_S24LE: return ma_format_s24;
30868 case MA_PA_SAMPLE_S32LE: return ma_format_s32;
30869 case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32;
30870 default: break;
30871 }
30872 } else {
30873 switch (format) {
30874 case MA_PA_SAMPLE_S16BE: return ma_format_s16;
30875 case MA_PA_SAMPLE_S24BE: return ma_format_s24;
30876 case MA_PA_SAMPLE_S32BE: return ma_format_s32;
30877 case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32;
30878 default: break;
30879 }
30880 }
30881
30882 /* Endian agnostic. */
30883 switch (format) {
30884 case MA_PA_SAMPLE_U8: return ma_format_u8;
30885 default: return ma_format_unknown;
30886 }
30887}
30888
30889static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position)
30890{
30891 switch (position)
30892 {
30893 case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE;
30894 case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO;
30895 case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
30896 case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
30897 case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
30898 case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER;
30899 case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT;
30900 case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT;
30901 case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE;
30902 case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
30903 case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
30904 case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
30905 case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
30906 case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0;
30907 case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1;
30908 case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2;
30909 case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3;
30910 case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4;
30911 case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5;
30912 case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6;
30913 case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7;
30914 case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8;
30915 case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9;
30916 case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10;
30917 case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11;
30918 case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12;
30919 case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13;
30920 case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14;
30921 case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15;
30922 case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16;
30923 case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17;
30924 case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18;
30925 case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19;
30926 case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20;
30927 case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21;
30928 case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22;
30929 case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23;
30930 case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24;
30931 case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25;
30932 case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26;
30933 case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27;
30934 case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28;
30935 case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29;
30936 case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30;
30937 case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31;
30938 case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
30939 case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
30940 case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
30941 case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
30942 case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
30943 case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
30944 case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
30945 default: return MA_CHANNEL_NONE;
30946 }
30947}
30948
30949#if 0
30950static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position)
30951{
30952 switch (position)
30953 {
30954 case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID;
30955 case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT;
30956 case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT;
30957 case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER;
30958 case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE;
30959 case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT;
30960 case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT;
30961 case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
30962 case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
30963 case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER;
30964 case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT;
30965 case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT;
30966 case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER;
30967 case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
30968 case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
30969 case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
30970 case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT;
30971 case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER;
30972 case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
30973 case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18;
30974 case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19;
30975 case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20;
30976 case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21;
30977 case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22;
30978 case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23;
30979 case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24;
30980 case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25;
30981 case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26;
30982 case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27;
30983 case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28;
30984 case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29;
30985 case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30;
30986 case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31;
30987 default: return (ma_pa_channel_position_t)position;
30988 }
30989}
30990#endif
30991
30992static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP)
30993{
30994 int resultPA;
30995 ma_pa_operation_state_t state;
30996
30997 MA_ASSERT(pContext != NULL);
30998 MA_ASSERT(pOP != NULL);
30999
31000 for (;;) {
31001 state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP);
31002 if (state != MA_PA_OPERATION_RUNNING) {
31003 break; /* Done. */
31004 }
31005
31006 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
31007 if (resultPA < 0) {
31008 return ma_result_from_pulse(resultPA);
31009 }
31010 }
31011
31012 return MA_SUCCESS;
31013}
31014
31015static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP)
31016{
31017 ma_result result;
31018
31019 if (pOP == NULL) {
31020 return MA_INVALID_ARGS;
31021 }
31022
31023 result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
31024 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
31025
31026 return result;
31027}
31028
31029static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext)
31030{
31031 int resultPA;
31032 ma_pa_context_state_t state;
31033
31034 for (;;) {
31035 state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext);
31036 if (state == MA_PA_CONTEXT_READY) {
31037 break; /* Done. */
31038 }
31039
31040 if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
31041 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context.");
31042 return MA_ERROR;
31043 }
31044
31045 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
31046 if (resultPA < 0) {
31047 return ma_result_from_pulse(resultPA);
31048 }
31049 }
31050
31051 /* Should never get here. */
31052 return MA_SUCCESS;
31053}
31054
31055static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream)
31056{
31057 int resultPA;
31058 ma_pa_stream_state_t state;
31059
31060 for (;;) {
31061 state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream);
31062 if (state == MA_PA_STREAM_READY) {
31063 break; /* Done. */
31064 }
31065
31066 if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) {
31067 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream.");
31068 return MA_ERROR;
31069 }
31070
31071 resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
31072 if (resultPA < 0) {
31073 return ma_result_from_pulse(resultPA);
31074 }
31075 }
31076
31077 return MA_SUCCESS;
31078}
31079
31080
31081static 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)
31082{
31083 ma_result result;
31084 ma_ptr pMainLoop;
31085 ma_ptr pPulseContext;
31086
31087 MA_ASSERT(ppMainLoop != NULL);
31088 MA_ASSERT(ppPulseContext != NULL);
31089
31090 /* 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. */
31091 pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
31092 if (pMainLoop == NULL) {
31093 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop.");
31095 }
31096
31097 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);
31098 if (pPulseContext == NULL) {
31099 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context.");
31100 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));
31102 }
31103
31104 /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */
31105 result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? MA_PA_CONTEXT_NOFLAGS : MA_PA_CONTEXT_NOAUTOSPAWN, NULL));
31106 if (result != MA_SUCCESS) {
31107 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context.");
31108 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));
31109 return result;
31110 }
31111
31112 /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */
31113 result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext);
31114 if (result != MA_SUCCESS) {
31115 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Waiting for connection failed.");
31116 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));
31117 return result;
31118 }
31119
31120 *ppMainLoop = pMainLoop;
31121 *ppPulseContext = pPulseContext;
31122
31123 return MA_SUCCESS;
31124}
31125
31126
31127static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
31128{
31129 ma_pa_sink_info* pInfoOut;
31130
31131 if (endOfList > 0) {
31132 return;
31133 }
31134
31135 /*
31136 There has been a report that indicates that pInfo can be null which results
31137 in a null pointer dereference below. We'll check for this for safety.
31138 */
31139 if (pInfo == NULL) {
31140 return;
31141 }
31142
31143 pInfoOut = (ma_pa_sink_info*)pUserData;
31144 MA_ASSERT(pInfoOut != NULL);
31145
31146 *pInfoOut = *pInfo;
31147
31148 (void)pPulseContext; /* Unused. */
31149}
31150
31151static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
31152{
31153 ma_pa_source_info* pInfoOut;
31154
31155 if (endOfList > 0) {
31156 return;
31157 }
31158
31159 /*
31160 There has been a report that indicates that pInfo can be null which results
31161 in a null pointer dereference below. We'll check for this for safety.
31162 */
31163 if (pInfo == NULL) {
31164 return;
31165 }
31166
31167 pInfoOut = (ma_pa_source_info*)pUserData;
31168 MA_ASSERT(pInfoOut != NULL);
31169
31170 *pInfoOut = *pInfo;
31171
31172 (void)pPulseContext; /* Unused. */
31173}
31174
31175#if 0
31176static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
31177{
31178 ma_device* pDevice;
31179
31180 if (endOfList > 0) {
31181 return;
31182 }
31183
31184 pDevice = (ma_device*)pUserData;
31185 MA_ASSERT(pDevice != NULL);
31186
31187 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1);
31188
31189 (void)pPulseContext; /* Unused. */
31190}
31191
31192static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
31193{
31194 ma_device* pDevice;
31195
31196 if (endOfList > 0) {
31197 return;
31198 }
31199
31200 pDevice = (ma_device*)pUserData;
31201 MA_ASSERT(pDevice != NULL);
31202
31203 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1);
31204
31205 (void)pPulseContext; /* Unused. */
31206}
31207#endif
31208
31209static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo)
31210{
31211 ma_pa_operation* pOP;
31212
31213 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);
31214 if (pOP == NULL) {
31215 return MA_ERROR;
31216 }
31217
31218 return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);
31219}
31220
31221static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo)
31222{
31223 ma_pa_operation* pOP;
31224
31225 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);
31226 if (pOP == NULL) {
31227 return MA_ERROR;
31228 }
31229
31230 return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);
31231}
31232
31233static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex)
31234{
31235 ma_result result;
31236
31237 MA_ASSERT(pContext != NULL);
31238 MA_ASSERT(pIndex != NULL);
31239
31240 if (pIndex != NULL) {
31241 *pIndex = (ma_uint32)-1;
31242 }
31243
31244 if (deviceType == ma_device_type_playback) {
31245 ma_pa_sink_info sinkInfo;
31246 result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo);
31247 if (result != MA_SUCCESS) {
31248 return result;
31249 }
31250
31251 if (pIndex != NULL) {
31252 *pIndex = sinkInfo.index;
31253 }
31254 }
31255
31256 if (deviceType == ma_device_type_capture) {
31257 ma_pa_source_info sourceInfo;
31258 result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo);
31259 if (result != MA_SUCCESS) {
31260 return result;
31261 }
31262
31263 if (pIndex != NULL) {
31264 *pIndex = sourceInfo.index;
31265 }
31266 }
31267
31268 return MA_SUCCESS;
31269}
31270
31271
31272typedef struct
31273{
31274 ma_context* pContext;
31276 void* pUserData;
31277 ma_bool32 isTerminated;
31278 ma_uint32 defaultDeviceIndexPlayback;
31279 ma_uint32 defaultDeviceIndexCapture;
31280} ma_context_enumerate_devices_callback_data__pulse;
31281
31282static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData)
31283{
31284 ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
31285 ma_device_info deviceInfo;
31286
31287 MA_ASSERT(pData != NULL);
31288
31289 if (endOfList || pData->isTerminated) {
31290 return;
31291 }
31292
31293 MA_ZERO_OBJECT(&deviceInfo);
31294
31295 /* The name from PulseAudio is the ID for miniaudio. */
31296 if (pSinkInfo->name != NULL) {
31297 ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1);
31298 }
31299
31300 /* The description from PulseAudio is the name for miniaudio. */
31301 if (pSinkInfo->description != NULL) {
31302 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1);
31303 }
31304
31305 if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) {
31306 deviceInfo.isDefault = MA_TRUE;
31307 }
31308
31309 pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData);
31310
31311 (void)pPulseContext; /* Unused. */
31312}
31313
31314static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData)
31315{
31316 ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
31317 ma_device_info deviceInfo;
31318
31319 MA_ASSERT(pData != NULL);
31320
31321 if (endOfList || pData->isTerminated) {
31322 return;
31323 }
31324
31325 MA_ZERO_OBJECT(&deviceInfo);
31326
31327 /* The name from PulseAudio is the ID for miniaudio. */
31328 if (pSourceInfo->name != NULL) {
31329 ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1);
31330 }
31331
31332 /* The description from PulseAudio is the name for miniaudio. */
31333 if (pSourceInfo->description != NULL) {
31334 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1);
31335 }
31336
31337 if (pSourceInfo->index == pData->defaultDeviceIndexCapture) {
31338 deviceInfo.isDefault = MA_TRUE;
31339 }
31340
31341 pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData);
31342
31343 (void)pPulseContext; /* Unused. */
31344}
31345
31346static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
31347{
31348 ma_result result = MA_SUCCESS;
31349 ma_context_enumerate_devices_callback_data__pulse callbackData;
31350 ma_pa_operation* pOP = NULL;
31351
31352 MA_ASSERT(pContext != NULL);
31353 MA_ASSERT(callback != NULL);
31354
31355 callbackData.pContext = pContext;
31356 callbackData.callback = callback;
31357 callbackData.pUserData = pUserData;
31358 callbackData.isTerminated = MA_FALSE;
31359 callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1;
31360 callbackData.defaultDeviceIndexCapture = (ma_uint32)-1;
31361
31362 /* We need to get the index of the default devices. */
31363 ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback);
31364 ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture);
31365
31366 /* Playback. */
31367 if (!callbackData.isTerminated) {
31368 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);
31369 if (pOP == NULL) {
31370 result = MA_ERROR;
31371 goto done;
31372 }
31373
31374 result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP);
31375 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
31376
31377 if (result != MA_SUCCESS) {
31378 goto done;
31379 }
31380 }
31381
31382
31383 /* Capture. */
31384 if (!callbackData.isTerminated) {
31385 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);
31386 if (pOP == NULL) {
31387 result = MA_ERROR;
31388 goto done;
31389 }
31390
31391 result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP);
31392 ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
31393
31394 if (result != MA_SUCCESS) {
31395 goto done;
31396 }
31397 }
31398
31399done:
31400 return result;
31401}
31402
31403
31404typedef struct
31405{
31406 ma_device_info* pDeviceInfo;
31407 ma_uint32 defaultDeviceIndex;
31408 ma_bool32 foundDevice;
31409} ma_context_get_device_info_callback_data__pulse;
31410
31411static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
31412{
31413 ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
31414
31415 if (endOfList > 0) {
31416 return;
31417 }
31418
31419 MA_ASSERT(pData != NULL);
31420 pData->foundDevice = MA_TRUE;
31421
31422 if (pInfo->name != NULL) {
31423 ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
31424 }
31425
31426 if (pInfo->description != NULL) {
31427 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
31428 }
31429
31430 /*
31431 We're just reporting a single data format here. I think technically PulseAudio might support
31432 all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to
31433 report the "native" device format.
31434 */
31435 pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format);
31436 pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels;
31437 pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate;
31438 pData->pDeviceInfo->nativeDataFormats[0].flags = 0;
31439 pData->pDeviceInfo->nativeDataFormatCount = 1;
31440
31441 if (pData->defaultDeviceIndex == pInfo->index) {
31442 pData->pDeviceInfo->isDefault = MA_TRUE;
31443 }
31444
31445 (void)pPulseContext; /* Unused. */
31446}
31447
31448static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
31449{
31450 ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
31451
31452 if (endOfList > 0) {
31453 return;
31454 }
31455
31456 MA_ASSERT(pData != NULL);
31457 pData->foundDevice = MA_TRUE;
31458
31459 if (pInfo->name != NULL) {
31460 ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
31461 }
31462
31463 if (pInfo->description != NULL) {
31464 ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
31465 }
31466
31467 /*
31468 We're just reporting a single data format here. I think technically PulseAudio might support
31469 all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to
31470 report the "native" device format.
31471 */
31472 pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format);
31473 pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels;
31474 pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate;
31475 pData->pDeviceInfo->nativeDataFormats[0].flags = 0;
31476 pData->pDeviceInfo->nativeDataFormatCount = 1;
31477
31478 if (pData->defaultDeviceIndex == pInfo->index) {
31479 pData->pDeviceInfo->isDefault = MA_TRUE;
31480 }
31481
31482 (void)pPulseContext; /* Unused. */
31483}
31484
31485static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
31486{
31487 ma_result result = MA_SUCCESS;
31488 ma_context_get_device_info_callback_data__pulse callbackData;
31489 ma_pa_operation* pOP = NULL;
31490 const char* pDeviceName = NULL;
31491
31492 MA_ASSERT(pContext != NULL);
31493
31494 callbackData.pDeviceInfo = pDeviceInfo;
31495 callbackData.foundDevice = MA_FALSE;
31496
31497 if (pDeviceID != NULL) {
31498 pDeviceName = pDeviceID->pulse;
31499 } else {
31500 pDeviceName = NULL;
31501 }
31502
31503 result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex);
31504
31505 if (deviceType == ma_device_type_playback) {
31506 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);
31507 } else {
31508 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);
31509 }
31510
31511 if (pOP != NULL) {
31512 ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);
31513 } else {
31514 result = MA_ERROR;
31515 goto done;
31516 }
31517
31518 if (!callbackData.foundDevice) {
31519 result = MA_NO_DEVICE;
31520 goto done;
31521 }
31522
31523done:
31524 return result;
31525}
31526
31527static ma_result ma_device_uninit__pulse(ma_device* pDevice)
31528{
31529 ma_context* pContext;
31530
31531 MA_ASSERT(pDevice != NULL);
31532
31533 pContext = pDevice->pContext;
31534 MA_ASSERT(pContext != NULL);
31535
31536 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
31537 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
31538 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
31539 }
31540
31541 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
31542 ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
31543 ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
31544 }
31545
31546 if (pDevice->type == ma_device_type_duplex) {
31547 ma_duplex_rb_uninit(&pDevice->duplexRB);
31548 }
31549
31550 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);
31551 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);
31552 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
31553
31554 return MA_SUCCESS;
31555}
31556
31557static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss)
31558{
31559 ma_pa_buffer_attr attr;
31560 attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels);
31561 attr.tlength = attr.maxlength / periods;
31562 attr.prebuf = (ma_uint32)-1;
31563 attr.minreq = (ma_uint32)-1;
31564 attr.fragsize = attr.maxlength / periods;
31565
31566 return attr;
31567}
31568
31569static 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)
31570{
31571 static ma_atomic_uint32 g_StreamCounter = { 0 };
31572 char actualStreamName[256];
31573
31574 if (pStreamName != NULL) {
31575 ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1);
31576 } else {
31577 const char* pBaseName = "miniaudio:";
31578 size_t baseNameLen = strlen(pBaseName);
31579 ma_strcpy_s(actualStreamName, sizeof(actualStreamName), pBaseName);
31580 ma_itoa_s((int)ma_atomic_uint32_get(&g_StreamCounter), actualStreamName + baseNameLen, sizeof(actualStreamName)-baseNameLen, 10);
31581 }
31582 ma_atomic_uint32_fetch_add(&g_StreamCounter, 1);
31583
31584 return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap);
31585}
31586
31587
31588static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData)
31589{
31590 ma_device* pDevice = (ma_device*)pUserData;
31591 ma_uint32 bpf;
31592 ma_uint32 deviceState;
31593 ma_uint64 frameCount;
31594 ma_uint64 framesProcessed;
31595
31596 MA_ASSERT(pDevice != NULL);
31597
31598 /*
31599 Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio
31600 can fire this callback before the stream has even started. Ridiculous.
31601 */
31602 deviceState = ma_device_get_state(pDevice);
31603 if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {
31604 return;
31605 }
31606
31608 MA_ASSERT(bpf > 0);
31609
31610 frameCount = byteCount / bpf;
31611 framesProcessed = 0;
31612
31613 while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) {
31614 const void* pMappedPCMFrames;
31615 size_t bytesMapped;
31616 ma_uint64 framesMapped;
31617
31618 int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped);
31619 if (pulseResult < 0) {
31620 break; /* Failed to map. Abort. */
31621 }
31622
31623 framesMapped = bytesMapped / bpf;
31624 if (framesMapped > 0) {
31625 if (pMappedPCMFrames != NULL) {
31626 ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped);
31627 } else {
31628 /* It's a hole. */
31629 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulse: Hole.\n");
31630 }
31631
31632 pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream);
31633 if (pulseResult < 0) {
31634 break; /* Failed to drop the buffer. */
31635 }
31636
31637 framesProcessed += framesMapped;
31638
31639 } else {
31640 /* Nothing was mapped. Just abort. */
31641 break;
31642 }
31643 }
31644}
31645
31646static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed)
31647{
31648 ma_result result = MA_SUCCESS;
31649 ma_uint64 framesProcessed = 0;
31650 size_t bytesMapped;
31651 ma_uint32 bpf;
31652 ma_uint32 deviceState;
31653
31654 MA_ASSERT(pDevice != NULL);
31655 MA_ASSERT(pStream != NULL);
31656
31658 MA_ASSERT(bpf > 0);
31659
31660 deviceState = ma_device_get_state(pDevice);
31661
31662 bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream);
31663 if (bytesMapped != (size_t)-1) {
31664 if (bytesMapped > 0) {
31665 ma_uint64 framesMapped;
31666 void* pMappedPCMFrames;
31667 int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped);
31668 if (pulseResult < 0) {
31669 result = ma_result_from_pulse(pulseResult);
31670 goto done;
31671 }
31672
31673 framesMapped = bytesMapped / bpf;
31674
31675 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. */
31676 ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped);
31677 } else {
31678 /* Device is not started. Write silence. */
31679 ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels);
31680 }
31681
31682 pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE);
31683 if (pulseResult < 0) {
31684 result = ma_result_from_pulse(pulseResult);
31685 goto done; /* Failed to write data to stream. */
31686 }
31687
31688 framesProcessed += framesMapped;
31689 } else {
31690 result = MA_SUCCESS; /* No data available for writing. */
31691 goto done;
31692 }
31693 } else {
31694 result = MA_ERROR; /* Failed to retrieve the writable size. Abort. */
31695 goto done;
31696 }
31697
31698done:
31699 if (pFramesProcessed != NULL) {
31700 *pFramesProcessed = framesProcessed;
31701 }
31702
31703 return result;
31704}
31705
31706static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData)
31707{
31708 ma_device* pDevice = (ma_device*)pUserData;
31709 ma_uint32 bpf;
31710 ma_uint64 frameCount;
31711 ma_uint64 framesProcessed;
31712 ma_uint32 deviceState;
31713 ma_result result;
31714
31715 MA_ASSERT(pDevice != NULL);
31716
31717 /*
31718 Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio
31719 can fire this callback before the stream has even started. Ridiculous.
31720 */
31721 deviceState = ma_device_get_state(pDevice);
31722 if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {
31723 return;
31724 }
31725
31727 MA_ASSERT(bpf > 0);
31728
31729 frameCount = byteCount / bpf;
31730 framesProcessed = 0;
31731
31732 while (framesProcessed < frameCount) {
31733 ma_uint64 framesProcessedThisIteration;
31734
31735 /* Don't keep trying to process frames if the device isn't started. */
31736 deviceState = ma_device_get_state(pDevice);
31737 if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {
31738 break;
31739 }
31740
31741 result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration);
31742 if (result != MA_SUCCESS) {
31743 break;
31744 }
31745
31746 framesProcessed += framesProcessedThisIteration;
31747 }
31748}
31749
31750static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData)
31751{
31752 ma_device* pDevice = (ma_device*)pUserData;
31753 int suspended;
31754
31755 (void)pStream;
31756
31757 suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream);
31758 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);
31759
31760 if (suspended < 0) {
31761 return;
31762 }
31763
31764 if (suspended == 1) {
31765 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n");
31766 ma_device__on_notification_stopped(pDevice);
31767 } else {
31768 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n");
31769 ma_device__on_notification_started(pDevice);
31770 }
31771}
31772
31773static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData)
31774{
31775 ma_device* pDevice = (ma_device*)pUserData;
31776
31777 (void)pStream;
31778 (void)pUserData;
31779
31780 ma_device__on_notification_rerouted(pDevice);
31781}
31782
31783static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
31784{
31785 /*
31786 There have been reports from users where buffers of < ~20ms result glitches when running through
31787 PipeWire. To work around this we're going to have to use a different default buffer size.
31788 */
31789 const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency = 25;
31790 const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE;
31791
31792 MA_ASSERT(nativeSampleRate != 0);
31793
31794 if (pDescriptor->periodSizeInFrames == 0) {
31795 if (pDescriptor->periodSizeInMilliseconds == 0) {
31796 if (performanceProfile == ma_performance_profile_low_latency) {
31797 return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate);
31798 } else {
31799 return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate);
31800 }
31801 } else {
31803 }
31804 } else {
31805 return pDescriptor->periodSizeInFrames;
31806 }
31807}
31808
31809static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
31810{
31811 /*
31812 Notes for PulseAudio:
31813
31814 - When both the period size in frames and milliseconds are 0, we default to miniaudio's
31815 default buffer sizes rather than leaving it up to PulseAudio because I don't trust
31816 PulseAudio to give us any kind of reasonable latency by default.
31817
31818 - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this
31819 flag, capture mode will just not work properly until you open another PulseAudio app.
31820 */
31821
31822 ma_result result = MA_SUCCESS;
31823 int error = 0;
31824 const char* devPlayback = NULL;
31825 const char* devCapture = NULL;
31827 ma_uint32 channels = 0;
31828 ma_uint32 sampleRate = 0;
31829 ma_pa_sink_info sinkInfo;
31830 ma_pa_source_info sourceInfo;
31831 ma_pa_sample_spec ss;
31832 ma_pa_channel_map cmap;
31833 ma_pa_buffer_attr attr;
31834 const ma_pa_sample_spec* pActualSS = NULL;
31835 const ma_pa_buffer_attr* pActualAttr = NULL;
31836 const ma_pa_channel_map* pActualChannelMap = NULL;
31837 ma_uint32 iChannel;
31838 int streamFlags;
31839
31840 MA_ASSERT(pDevice != NULL);
31841 MA_ZERO_OBJECT(&pDevice->pulse);
31842
31843 if (pConfig->deviceType == ma_device_type_loopback) {
31845 }
31846
31847 /* No exclusive mode with the PulseAudio backend. */
31851 }
31852
31853 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
31854 if (pDescriptorPlayback->pDeviceID != NULL) {
31855 devPlayback = pDescriptorPlayback->pDeviceID->pulse;
31856 }
31857
31858 format = pDescriptorPlayback->format;
31859 channels = pDescriptorPlayback->channels;
31860 sampleRate = pDescriptorPlayback->sampleRate;
31861 }
31862
31863 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
31864 if (pDescriptorCapture->pDeviceID != NULL) {
31865 devCapture = pDescriptorCapture->pDeviceID->pulse;
31866 }
31867
31868 format = pDescriptorCapture->format;
31869 channels = pDescriptorCapture->channels;
31870 sampleRate = pDescriptorCapture->sampleRate;
31871 }
31872
31873
31874
31875 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);
31876 if (result != MA_SUCCESS) {
31877 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize PA mainloop and context for device.\n");
31878 return result;
31879 }
31880
31881 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
31882 result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo);
31883 if (result != MA_SUCCESS) {
31884 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device.");
31885 goto on_error0;
31886 }
31887
31888 ss = sourceInfo.sample_spec;
31889 cmap = sourceInfo.channel_map;
31890
31891 /* Use the requested channel count if we have one. */
31892 if (pDescriptorCapture->channels != 0) {
31893 ss.channels = pDescriptorCapture->channels;
31894 }
31895
31896 /* PulseAudio has a maximum channel count of 32. We'll get a crash if this is exceeded. */
31897 if (ss.channels > 32) {
31898 ss.channels = 32;
31899 }
31900
31901 /* Use a default channel map. */
31902 ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, (ma_pa_channel_map_def_t)pConfig->pulse.channelMap);
31903
31904 /* Use the requested sample rate if one was specified. */
31905 if (pDescriptorCapture->sampleRate != 0) {
31906 ss.rate = pDescriptorCapture->sampleRate;
31907 }
31908 streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY;
31909
31910 if (ma_format_from_pulse(ss.format) == ma_format_unknown) {
31911 if (ma_is_little_endian()) {
31912 ss.format = MA_PA_SAMPLE_FLOAT32LE;
31913 } else {
31914 ss.format = MA_PA_SAMPLE_FLOAT32BE;
31915 }
31916 streamFlags |= MA_PA_STREAM_FIX_FORMAT;
31917 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");
31918 }
31919 if (ss.rate == 0) {
31920 ss.rate = MA_DEFAULT_SAMPLE_RATE;
31921 streamFlags |= MA_PA_STREAM_FIX_RATE;
31922 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate);
31923 }
31924 if (ss.channels == 0) {
31925 ss.channels = MA_DEFAULT_CHANNELS;
31926 streamFlags |= MA_PA_STREAM_FIX_CHANNELS;
31927 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels);
31928 }
31929
31930 /* We now have enough information to calculate our actual period size in frames. */
31931 pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile);
31932
31933 attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss);
31934 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);
31935
31936 pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap);
31937 if (pDevice->pulse.pStreamCapture == NULL) {
31938 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.\n");
31939 result = MA_ERROR;
31940 goto on_error0;
31941 }
31942
31943
31944 /* The callback needs to be set before connecting the stream. */
31945 ((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);
31946
31947 /* State callback for checking when the device has been corked. */
31948 ((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);
31949
31950 /* Rerouting notification. */
31951 ((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);
31952
31953
31954 /* Connect after we've got all of our internal state set up. */
31955 if (devCapture != NULL) {
31956 streamFlags |= MA_PA_STREAM_DONT_MOVE;
31957 }
31958
31959 error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, (ma_pa_stream_flags_t)streamFlags);
31960 if (error != MA_PA_OK) {
31961 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream.");
31962 result = ma_result_from_pulse(error);
31963 goto on_error1;
31964 }
31965
31966 result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture);
31967 if (result != MA_SUCCESS) {
31968 goto on_error2;
31969 }
31970
31971
31972 /* Internal format. */
31973 pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
31974 if (pActualSS != NULL) {
31975 ss = *pActualSS;
31976 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);
31977 } else {
31978 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve capture sample spec.\n");
31979 }
31980
31981 pDescriptorCapture->format = ma_format_from_pulse(ss.format);
31982 pDescriptorCapture->channels = ss.channels;
31983 pDescriptorCapture->sampleRate = ss.rate;
31984
31985 if (pDescriptorCapture->format == ma_format_unknown || pDescriptorCapture->channels == 0 || pDescriptorCapture->sampleRate == 0) {
31986 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);
31987 result = MA_ERROR;
31988 goto on_error4;
31989 }
31990
31991
31992 /* Internal channel map. */
31993 pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
31994 if (pActualChannelMap == NULL) {
31995 pActualChannelMap = &cmap; /* Fallback just in case. */
31996 }
31997
31998 /*
31999 Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting
32000 the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono
32001 and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For
32002 all other channel counts we need to just put up with whatever PipeWire reports and hope it gets
32003 fixed sooner than later. I might remove this hack later.
32004 */
32005 if (pDescriptorCapture->channels > 2) {
32006 for (iChannel = 0; iChannel < pDescriptorCapture->channels; iChannel += 1) {
32007 pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]);
32008 }
32009 } else {
32010 /* Hack for mono and stereo. */
32011 if (pDescriptorCapture->channels == 1) {
32012 pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO;
32013 } else if (pDescriptorCapture->channels == 2) {
32014 pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT;
32015 pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
32016 } else {
32017 MA_ASSERT(MA_FALSE); /* Should never hit this. */
32018 }
32019 }
32020
32021
32022 /* Buffer. */
32023 pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
32024 if (pActualAttr != NULL) {
32025 attr = *pActualAttr;
32026 }
32027
32028 if (attr.fragsize > 0) {
32029 pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1);
32030 } else {
32031 pDescriptorCapture->periodCount = 1;
32032 }
32033
32034 pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount;
32035 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);
32036 }
32037
32038 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
32039 result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo);
32040 if (result != MA_SUCCESS) {
32041 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.\n");
32042 goto on_error2;
32043 }
32044
32045 ss = sinkInfo.sample_spec;
32046 cmap = sinkInfo.channel_map;
32047
32048 /* Use the requested channel count if we have one. */
32049 if (pDescriptorPlayback->channels != 0) {
32050 ss.channels = pDescriptorPlayback->channels;
32051 }
32052
32053 /* PulseAudio has a maximum channel count of 32. We'll get a crash if this is exceeded. */
32054 if (ss.channels > 32) {
32055 ss.channels = 32;
32056 }
32057
32058 /* Use a default channel map. */
32059 ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, (ma_pa_channel_map_def_t)pConfig->pulse.channelMap);
32060
32061
32062 /* Use the requested sample rate if one was specified. */
32063 if (pDescriptorPlayback->sampleRate != 0) {
32064 ss.rate = pDescriptorPlayback->sampleRate;
32065 }
32066
32067 streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY;
32068 if (ma_format_from_pulse(ss.format) == ma_format_unknown) {
32069 if (ma_is_little_endian()) {
32070 ss.format = MA_PA_SAMPLE_FLOAT32LE;
32071 } else {
32072 ss.format = MA_PA_SAMPLE_FLOAT32BE;
32073 }
32074 streamFlags |= MA_PA_STREAM_FIX_FORMAT;
32075 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");
32076 }
32077 if (ss.rate == 0) {
32078 ss.rate = MA_DEFAULT_SAMPLE_RATE;
32079 streamFlags |= MA_PA_STREAM_FIX_RATE;
32080 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate);
32081 }
32082 if (ss.channels == 0) {
32083 ss.channels = MA_DEFAULT_CHANNELS;
32084 streamFlags |= MA_PA_STREAM_FIX_CHANNELS;
32085 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels);
32086 }
32087
32088 /* We now have enough information to calculate the actual buffer size in frames. */
32089 pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile);
32090
32091 attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss);
32092
32093 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);
32094
32095 pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap);
32096 if (pDevice->pulse.pStreamPlayback == NULL) {
32097 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.\n");
32098 result = MA_ERROR;
32099 goto on_error2;
32100 }
32101
32102
32103 /*
32104 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
32105 device state of ma_device_state_uninitialized.
32106 */
32107 ((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);
32108
32109 /* State callback for checking when the device has been corked. */
32110 ((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);
32111
32112 /* Rerouting notification. */
32113 ((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);
32114
32115
32116 /* Connect after we've got all of our internal state set up. */
32117 if (devPlayback != NULL) {
32118 streamFlags |= MA_PA_STREAM_DONT_MOVE;
32119 }
32120
32121 error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, (ma_pa_stream_flags_t)streamFlags, NULL, NULL);
32122 if (error != MA_PA_OK) {
32123 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream.");
32124 result = ma_result_from_pulse(error);
32125 goto on_error3;
32126 }
32127
32128 result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback);
32129 if (result != MA_SUCCESS) {
32130 goto on_error3;
32131 }
32132
32133
32134 /* Internal format. */
32135 pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
32136 if (pActualSS != NULL) {
32137 ss = *pActualSS;
32138 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);
32139 } else {
32140 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve playback sample spec.\n");
32141 }
32142
32143 pDescriptorPlayback->format = ma_format_from_pulse(ss.format);
32144 pDescriptorPlayback->channels = ss.channels;
32145 pDescriptorPlayback->sampleRate = ss.rate;
32146
32147 if (pDescriptorPlayback->format == ma_format_unknown || pDescriptorPlayback->channels == 0 || pDescriptorPlayback->sampleRate == 0) {
32148 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);
32149 result = MA_ERROR;
32150 goto on_error4;
32151 }
32152
32153
32154 /* Internal channel map. */
32155 pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
32156 if (pActualChannelMap == NULL) {
32157 pActualChannelMap = &cmap; /* Fallback just in case. */
32158 }
32159
32160 /*
32161 Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting
32162 the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono
32163 and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For
32164 all other channel counts we need to just put up with whatever PipeWire reports and hope it gets
32165 fixed sooner than later. I might remove this hack later.
32166 */
32167 if (pDescriptorPlayback->channels > 2) {
32168 for (iChannel = 0; iChannel < pDescriptorPlayback->channels; iChannel += 1) {
32169 pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]);
32170 }
32171 } else {
32172 /* Hack for mono and stereo. */
32173 if (pDescriptorPlayback->channels == 1) {
32174 pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO;
32175 } else if (pDescriptorPlayback->channels == 2) {
32176 pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT;
32177 pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
32178 } else {
32179 MA_ASSERT(MA_FALSE); /* Should never hit this. */
32180 }
32181 }
32182
32183
32184 /* Buffer. */
32185 pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
32186 if (pActualAttr != NULL) {
32187 attr = *pActualAttr;
32188 }
32189
32190 if (attr.tlength > 0) {
32191 pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.tlength, 1);
32192 } else {
32193 pDescriptorPlayback->periodCount = 1;
32194 }
32195
32196 pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount;
32197 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);
32198 }
32199
32200
32201 /*
32202 We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main
32203 part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for
32204 us later on because that will only do it if it's a fully asynchronous backend - i.e. the
32205 onDeviceDataLoop callback is NULL, which is not the case for PulseAudio.
32206 */
32207 if (pConfig->deviceType == ma_device_type_duplex) {
32208 ma_format rbFormat = (format != ma_format_unknown) ? format : pDescriptorCapture->format;
32209 ma_uint32 rbChannels = (channels > 0) ? channels : pDescriptorCapture->channels;
32210 ma_uint32 rbSampleRate = (sampleRate > 0) ? sampleRate : pDescriptorCapture->sampleRate;
32211
32212 result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);
32213 if (result != MA_SUCCESS) {
32214 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer. %s.\n", ma_result_description(result));
32215 goto on_error4;
32216 }
32217 }
32218
32219 return MA_SUCCESS;
32220
32221
32222on_error4:
32223 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
32224 ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
32225 }
32226on_error3:
32227 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
32228 ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
32229 }
32230on_error2:
32231 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
32232 ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
32233 }
32234on_error1:
32235 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
32236 ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
32237 }
32238on_error0:
32239 return result;
32240}
32241
32242
32243static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData)
32244{
32245 ma_bool32* pIsSuccessful = (ma_bool32*)pUserData;
32246 MA_ASSERT(pIsSuccessful != NULL);
32247
32248 *pIsSuccessful = (ma_bool32)success;
32249
32250 (void)pStream; /* Unused. */
32251}
32252
32253static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork)
32254{
32255 ma_context* pContext = pDevice->pContext;
32256 ma_bool32 wasSuccessful;
32257 ma_pa_stream* pStream;
32258 ma_pa_operation* pOP;
32259 ma_result result;
32260
32261 /* This should not be called with a duplex device type. */
32262 if (deviceType == ma_device_type_duplex) {
32263 return MA_INVALID_ARGS;
32264 }
32265
32266 wasSuccessful = MA_FALSE;
32267
32268 pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback);
32269 MA_ASSERT(pStream != NULL);
32270
32271 pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful);
32272 if (pOP == NULL) {
32273 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream.");
32274 return MA_ERROR;
32275 }
32276
32277 result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP);
32278 if (result != MA_SUCCESS) {
32279 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork.");
32280 return result;
32281 }
32282
32283 if (!wasSuccessful) {
32284 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to %s PulseAudio stream.", (cork) ? "stop" : "start");
32285 return MA_ERROR;
32286 }
32287
32288 return MA_SUCCESS;
32289}
32290
32291static ma_result ma_device_start__pulse(ma_device* pDevice)
32292{
32293 ma_result result;
32294
32295 MA_ASSERT(pDevice != NULL);
32296
32297 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
32298 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0);
32299 if (result != MA_SUCCESS) {
32300 return result;
32301 }
32302 }
32303
32304 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
32305 /*
32306 We need to fill some data before uncorking. Not doing this will result in the write callback
32307 never getting fired. We're not going to abort if writing fails because I still want the device
32308 to get uncorked.
32309 */
32310 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.*/
32311
32312 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0);
32313 if (result != MA_SUCCESS) {
32314 return result;
32315 }
32316 }
32317
32318 return MA_SUCCESS;
32319}
32320
32321static ma_result ma_device_stop__pulse(ma_device* pDevice)
32322{
32323 ma_result result;
32324
32325 MA_ASSERT(pDevice != NULL);
32326
32327 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
32328 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1);
32329 if (result != MA_SUCCESS) {
32330 return result;
32331 }
32332 }
32333
32334 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
32335 /*
32336 Ideally we would drain the device here, but there's been cases where PulseAudio seems to be
32337 broken on some systems to the point where no audio processing seems to happen. When this
32338 happens, draining never completes and we get stuck here. For now I'm disabling draining of
32339 the device so we don't just freeze the application.
32340 */
32341 #if 0
32342 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);
32343 ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP);
32344 #endif
32345
32346 result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1);
32347 if (result != MA_SUCCESS) {
32348 return result;
32349 }
32350 }
32351
32352 return MA_SUCCESS;
32353}
32354
32355static ma_result ma_device_data_loop__pulse(ma_device* pDevice)
32356{
32357 int resultPA;
32358
32359 MA_ASSERT(pDevice != NULL);
32360
32361 /* NOTE: Don't start the device here. It'll be done at a higher level. */
32362
32363 /*
32364 All data is handled through callbacks. All we need to do is iterate over the main loop and let
32365 the callbacks deal with it.
32366 */
32367 while (ma_device_get_state(pDevice) == ma_device_state_started) {
32368 resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
32369 if (resultPA < 0) {
32370 break;
32371 }
32372 }
32373
32374 /* NOTE: Don't stop the device here. It'll be done at a higher level. */
32375 return MA_SUCCESS;
32376}
32377
32378static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice)
32379{
32380 MA_ASSERT(pDevice != NULL);
32381
32382 ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
32383
32384 return MA_SUCCESS;
32385}
32386
32387static ma_result ma_context_uninit__pulse(ma_context* pContext)
32388{
32389 MA_ASSERT(pContext != NULL);
32390 MA_ASSERT(pContext->backend == ma_backend_pulseaudio);
32391
32392 ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext);
32393 ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext);
32394 ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop);
32395
32396 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
32397 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
32398
32399#ifndef MA_NO_RUNTIME_LINKING
32400 ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO);
32401#endif
32402
32403 return MA_SUCCESS;
32404}
32405
32406static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
32407{
32408 ma_result result;
32409#ifndef MA_NO_RUNTIME_LINKING
32410 const char* libpulseNames[] = {
32411 "libpulse.so",
32412 "libpulse.so.0"
32413 };
32414 size_t i;
32415
32416 for (i = 0; i < ma_countof(libpulseNames); ++i) {
32417 pContext->pulse.pulseSO = ma_dlopen(ma_context_get_log(pContext), libpulseNames[i]);
32418 if (pContext->pulse.pulseSO != NULL) {
32419 break;
32420 }
32421 }
32422
32423 if (pContext->pulse.pulseSO == NULL) {
32424 return MA_NO_BACKEND;
32425 }
32426
32427 pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_new");
32428 pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_free");
32429 pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_quit");
32430 pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_get_api");
32431 pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_iterate");
32432 pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_wakeup");
32433 pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_new");
32434 pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_free");
32435 pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_start");
32436 pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_stop");
32437 pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_lock");
32438 pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock");
32439 pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_wait");
32440 pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_signal");
32441 pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_accept");
32442 pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval");
32443 pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api");
32444 pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread");
32445 pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name");
32446 pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_new");
32447 pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_unref");
32448 pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_connect");
32449 pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_disconnect");
32450 pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_set_state_callback");
32451 pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_state");
32452 pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_list");
32453 pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_list");
32454 pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name");
32455 pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_by_name");
32456 pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_unref");
32457 pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_get_state");
32458 pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_init_extend");
32459 pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_valid");
32460 pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_compatible");
32461 pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_new");
32462 pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_unref");
32463 pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_playback");
32464 pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_record");
32465 pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_disconnect");
32466 pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_state");
32467 pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_sample_spec");
32468 pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_channel_map");
32469 pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_buffer_attr");
32470 pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_buffer_attr");
32471 pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_device_name");
32472 pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_write_callback");
32473 pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_read_callback");
32474 pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_suspended_callback");
32475 pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_moved_callback");
32476 pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_suspended");
32477 pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_flush");
32478 pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drain");
32479 pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_corked");
32480 pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_cork");
32481 pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_trigger");
32482 pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_begin_write");
32483 pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_write");
32484 pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_peek");
32485 pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drop");
32486 pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_writable_size");
32487 pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_readable_size");
32488#else
32489 /* This strange assignment system is just for type safety. */
32490 ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new;
32491 ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free;
32492 ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit;
32493 ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api;
32494 ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate;
32495 ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup;
32496 ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new;
32497 ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free;
32498 ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start;
32499 ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop;
32500 ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock;
32501 ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock;
32502 ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait;
32503 ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal;
32504 ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept;
32505 ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval;
32506 ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api;
32507 ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread;
32508 ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name;
32509 ma_pa_context_new_proc _pa_context_new = pa_context_new;
32510 ma_pa_context_unref_proc _pa_context_unref = pa_context_unref;
32511 ma_pa_context_connect_proc _pa_context_connect = pa_context_connect;
32512 ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect;
32513 ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback;
32514 ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state;
32515 ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list;
32516 ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list;
32517 ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name;
32518 ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name;
32519 ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref;
32520 ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state;
32521 ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend;
32522 ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid;
32523 ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible;
32524 ma_pa_stream_new_proc _pa_stream_new = pa_stream_new;
32525 ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref;
32526 ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback;
32527 ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record;
32528 ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect;
32529 ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state;
32530 ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec;
32531 ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map;
32532 ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr;
32533 ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr;
32534 ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name;
32535 ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback;
32536 ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback;
32537 ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback;
32538 ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback;
32539 ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended;
32540 ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush;
32541 ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain;
32542 ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked;
32543 ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork;
32544 ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger;
32545 ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write;
32546 ma_pa_stream_write_proc _pa_stream_write = pa_stream_write;
32547 ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek;
32548 ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop;
32549 ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size;
32550 ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size;
32551
32552 pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new;
32553 pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free;
32554 pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit;
32555 pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api;
32556 pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate;
32557 pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup;
32558 pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new;
32559 pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free;
32560 pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start;
32561 pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop;
32562 pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock;
32563 pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock;
32564 pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait;
32565 pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal;
32566 pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept;
32567 pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval;
32568 pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api;
32569 pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread;
32570 pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name;
32571 pContext->pulse.pa_context_new = (ma_proc)_pa_context_new;
32572 pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref;
32573 pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect;
32574 pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect;
32575 pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback;
32576 pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state;
32577 pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list;
32578 pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list;
32579 pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name;
32580 pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name;
32581 pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref;
32582 pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state;
32583 pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend;
32584 pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid;
32585 pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible;
32586 pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new;
32587 pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref;
32588 pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback;
32589 pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record;
32590 pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect;
32591 pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state;
32592 pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec;
32593 pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map;
32594 pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr;
32595 pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr;
32596 pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name;
32597 pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback;
32598 pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback;
32599 pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback;
32600 pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback;
32601 pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended;
32602 pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush;
32603 pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain;
32604 pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked;
32605 pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork;
32606 pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger;
32607 pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write;
32608 pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write;
32609 pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek;
32610 pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop;
32611 pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size;
32612 pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size;
32613#endif
32614
32615 /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */
32616 pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks);
32617 if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) {
32618 return MA_OUT_OF_MEMORY;
32619 }
32620
32621 pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks);
32622 if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) {
32623 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
32624 return MA_OUT_OF_MEMORY;
32625 }
32626
32627 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);
32628 if (result != MA_SUCCESS) {
32629 ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
32630 ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
32631 #ifndef MA_NO_RUNTIME_LINKING
32632 ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO);
32633 #endif
32634 return result;
32635 }
32636
32637 /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */
32638 pCallbacks->onContextInit = ma_context_init__pulse;
32639 pCallbacks->onContextUninit = ma_context_uninit__pulse;
32640 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse;
32641 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse;
32642 pCallbacks->onDeviceInit = ma_device_init__pulse;
32643 pCallbacks->onDeviceUninit = ma_device_uninit__pulse;
32644 pCallbacks->onDeviceStart = ma_device_start__pulse;
32645 pCallbacks->onDeviceStop = ma_device_stop__pulse;
32646 pCallbacks->onDeviceRead = NULL; /* Not used because we're implementing onDeviceDataLoop. */
32647 pCallbacks->onDeviceWrite = NULL; /* Not used because we're implementing onDeviceDataLoop. */
32648 pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse;
32649 pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse;
32650
32651 return MA_SUCCESS;
32652}
32653#endif
32654
32655
32656/******************************************************************************
32657
32658JACK Backend
32659
32660******************************************************************************/
32661#ifdef MA_HAS_JACK
32662
32663/* It is assumed jack.h is available when compile-time linking is being used. */
32664#ifdef MA_NO_RUNTIME_LINKING
32665#include <jack/jack.h>
32666
32667typedef jack_nframes_t ma_jack_nframes_t;
32668typedef jack_options_t ma_jack_options_t;
32669typedef jack_status_t ma_jack_status_t;
32670typedef jack_client_t ma_jack_client_t;
32671typedef jack_port_t ma_jack_port_t;
32672typedef JackProcessCallback ma_JackProcessCallback;
32673typedef JackBufferSizeCallback ma_JackBufferSizeCallback;
32674typedef JackShutdownCallback ma_JackShutdownCallback;
32675#define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE
32676#define ma_JackNullOption JackNullOption
32677#define ma_JackNoStartServer JackNoStartServer
32678#define ma_JackPortIsInput JackPortIsInput
32679#define ma_JackPortIsOutput JackPortIsOutput
32680#define ma_JackPortIsPhysical JackPortIsPhysical
32681#else
32682typedef ma_uint32 ma_jack_nframes_t;
32683typedef int ma_jack_options_t;
32684typedef int ma_jack_status_t;
32685typedef struct ma_jack_client_t ma_jack_client_t;
32686typedef struct ma_jack_port_t ma_jack_port_t;
32687typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg);
32688typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg);
32689typedef void (* ma_JackShutdownCallback) (void* arg);
32690#define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio"
32691#define ma_JackNullOption 0
32692#define ma_JackNoStartServer 1
32693#define ma_JackPortIsInput 1
32694#define ma_JackPortIsOutput 2
32695#define ma_JackPortIsPhysical 4
32696#endif
32697
32698typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...);
32699typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client);
32700typedef int (* ma_jack_client_name_size_proc) (void);
32701typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg);
32702typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg);
32703typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg);
32704typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client);
32705typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client);
32706typedef 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);
32707typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client);
32708typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client);
32709typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port);
32710typedef 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);
32711typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port);
32712typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes);
32713typedef void (* ma_jack_free_proc) (void* ptr);
32714
32715static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient)
32716{
32717 size_t maxClientNameSize;
32718 char clientName[256];
32719 ma_jack_status_t status;
32720 ma_jack_client_t* pClient;
32721
32722 MA_ASSERT(pContext != NULL);
32723 MA_ASSERT(ppClient != NULL);
32724
32725 if (ppClient) {
32726 *ppClient = NULL;
32727 }
32728
32729 maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */
32730 ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1);
32731
32732 pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? ma_JackNullOption : ma_JackNoStartServer, &status, NULL);
32733 if (pClient == NULL) {
32735 }
32736
32737 if (ppClient) {
32738 *ppClient = pClient;
32739 }
32740
32741 return MA_SUCCESS;
32742}
32743
32744
32745static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
32746{
32747 ma_bool32 cbResult = MA_TRUE;
32748
32749 MA_ASSERT(pContext != NULL);
32750 MA_ASSERT(callback != NULL);
32751
32752 /* Playback. */
32753 if (cbResult) {
32754 ma_device_info deviceInfo;
32755 MA_ZERO_OBJECT(&deviceInfo);
32756 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
32757 deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */
32758 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
32759 }
32760
32761 /* Capture. */
32762 if (cbResult) {
32763 ma_device_info deviceInfo;
32764 MA_ZERO_OBJECT(&deviceInfo);
32765 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
32766 deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */
32767 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
32768 }
32769
32770 (void)cbResult; /* For silencing a static analysis warning. */
32771
32772 return MA_SUCCESS;
32773}
32774
32775static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
32776{
32777 ma_jack_client_t* pClient;
32778 ma_result result;
32779 const char** ppPorts;
32780
32781 MA_ASSERT(pContext != NULL);
32782
32783 if (pDeviceID != NULL && pDeviceID->jack != 0) {
32784 return MA_NO_DEVICE; /* Don't know the device. */
32785 }
32786
32787 /* Name / Description */
32788 if (deviceType == ma_device_type_playback) {
32789 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
32790 } else {
32791 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
32792 }
32793
32794 /* Jack only uses default devices. */
32795 pDeviceInfo->isDefault = MA_TRUE;
32796
32797 /* Jack only supports f32 and has a specific channel count and sample rate. */
32798 pDeviceInfo->nativeDataFormats[0].format = ma_format_f32;
32799
32800 /* The channel count and sample rate can only be determined by opening the device. */
32801 result = ma_context_open_client__jack(pContext, &pClient);
32802 if (result != MA_SUCCESS) {
32803 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.");
32804 return result;
32805 }
32806
32807 pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient);
32808 pDeviceInfo->nativeDataFormats[0].channels = 0;
32809
32810 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));
32811 if (ppPorts == NULL) {
32812 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
32813 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.");
32815 }
32816
32817 while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) {
32818 pDeviceInfo->nativeDataFormats[0].channels += 1;
32819 }
32820
32821 pDeviceInfo->nativeDataFormats[0].flags = 0;
32822 pDeviceInfo->nativeDataFormatCount = 1;
32823
32824 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
32825 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
32826
32827 (void)pContext;
32828 return MA_SUCCESS;
32829}
32830
32831
32832static ma_result ma_device_uninit__jack(ma_device* pDevice)
32833{
32834 ma_context* pContext;
32835
32836 MA_ASSERT(pDevice != NULL);
32837
32838 pContext = pDevice->pContext;
32839 MA_ASSERT(pContext != NULL);
32840
32841 if (pDevice->jack.pClient != NULL) {
32842 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient);
32843 }
32844
32845 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
32846 ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
32847 ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks);
32848 }
32849
32850 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
32851 ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
32852 ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks);
32853 }
32854
32855 return MA_SUCCESS;
32856}
32857
32858static void ma_device__jack_shutdown_callback(void* pUserData)
32859{
32860 /* JACK died. Stop the device. */
32861 ma_device* pDevice = (ma_device*)pUserData;
32862 MA_ASSERT(pDevice != NULL);
32863
32864 ma_device_stop(pDevice);
32865}
32866
32867static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData)
32868{
32869 ma_device* pDevice = (ma_device*)pUserData;
32870 MA_ASSERT(pDevice != NULL);
32871
32872 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
32873 size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
32874 float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks);
32875 if (pNewBuffer == NULL) {
32876 return MA_OUT_OF_MEMORY;
32877 }
32878
32879 ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
32880
32881 pDevice->jack.pIntermediaryBufferCapture = pNewBuffer;
32882 pDevice->playback.internalPeriodSizeInFrames = frameCount;
32883 }
32884
32885 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
32886 size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
32887 float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks);
32888 if (pNewBuffer == NULL) {
32889 return MA_OUT_OF_MEMORY;
32890 }
32891
32892 ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
32893
32894 pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer;
32895 pDevice->playback.internalPeriodSizeInFrames = frameCount;
32896 }
32897
32898 return 0;
32899}
32900
32901static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData)
32902{
32903 ma_device* pDevice;
32904 ma_context* pContext;
32905 ma_uint32 iChannel;
32906
32907 pDevice = (ma_device*)pUserData;
32908 MA_ASSERT(pDevice != NULL);
32909
32910 pContext = pDevice->pContext;
32911 MA_ASSERT(pContext != NULL);
32912
32913 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
32914 /* Channels need to be interleaved. */
32915 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
32916 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);
32917 if (pSrc != NULL) {
32918 float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel;
32919 ma_jack_nframes_t iFrame;
32920 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
32921 *pDst = *pSrc;
32922
32923 pDst += pDevice->capture.internalChannels;
32924 pSrc += 1;
32925 }
32926 }
32927 }
32928
32929 ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount);
32930 }
32931
32932 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
32933 ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount);
32934
32935 /* Channels need to be deinterleaved. */
32936 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
32937 float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount);
32938 if (pDst != NULL) {
32939 const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel;
32940 ma_jack_nframes_t iFrame;
32941 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
32942 *pDst = *pSrc;
32943
32944 pDst += 1;
32945 pSrc += pDevice->playback.internalChannels;
32946 }
32947 }
32948 }
32949 }
32950
32951 return 0;
32952}
32953
32954static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
32955{
32956 ma_result result;
32957 ma_uint32 periodSizeInFrames;
32958
32959 MA_ASSERT(pConfig != NULL);
32960 MA_ASSERT(pDevice != NULL);
32961
32962 if (pConfig->deviceType == ma_device_type_loopback) {
32963 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported.");
32965 }
32966
32967 /* Only supporting default devices with JACK. */
32968 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) ||
32969 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) {
32970 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported.");
32971 return MA_NO_DEVICE;
32972 }
32973
32974 /* No exclusive mode with the JACK backend. */
32975 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
32976 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
32977 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported.");
32979 }
32980
32981 /* Open the client. */
32982 result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient);
32983 if (result != MA_SUCCESS) {
32984 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.");
32985 return result;
32986 }
32987
32988 /* Callbacks. */
32989 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) {
32990 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback.");
32992 }
32993 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) {
32994 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback.");
32996 }
32997
32998 ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice);
32999
33000
33001 /* The buffer size in frames can change. */
33002 periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient);
33003
33004 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
33005 ma_uint32 iPort;
33006 const char** ppPorts;
33007
33008 pDescriptorCapture->format = ma_format_f32;
33009 pDescriptorCapture->channels = 0;
33010 pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
33011 ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
33012
33013 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);
33014 if (ppPorts == NULL) {
33015 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.");
33017 }
33018
33019 /* Need to count the number of ports first so we can allocate some memory. */
33020 while (ppPorts[pDescriptorCapture->channels] != NULL) {
33021 pDescriptorCapture->channels += 1;
33022 }
33023
33024 pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks);
33025 if (pDevice->jack.ppPortsCapture == NULL) {
33026 return MA_OUT_OF_MEMORY;
33027 }
33028
33029 for (iPort = 0; iPort < pDescriptorCapture->channels; iPort += 1) {
33030 char name[64];
33031 ma_strcpy_s(name, sizeof(name), "capture");
33032 ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */
33033
33034 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);
33035 if (pDevice->jack.ppPortsCapture[iPort] == NULL) {
33036 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
33037 ma_device_uninit__jack(pDevice);
33038 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.");
33040 }
33041 }
33042
33043 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
33044
33045 pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
33046 pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */
33047
33048 pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks);
33049 if (pDevice->jack.pIntermediaryBufferCapture == NULL) {
33050 ma_device_uninit__jack(pDevice);
33051 return MA_OUT_OF_MEMORY;
33052 }
33053 }
33054
33055 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
33056 ma_uint32 iPort;
33057 const char** ppPorts;
33058
33059 pDescriptorPlayback->format = ma_format_f32;
33060 pDescriptorPlayback->channels = 0;
33061 pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
33062 ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);
33063
33064 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);
33065 if (ppPorts == NULL) {
33066 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.");
33068 }
33069
33070 /* Need to count the number of ports first so we can allocate some memory. */
33071 while (ppPorts[pDescriptorPlayback->channels] != NULL) {
33072 pDescriptorPlayback->channels += 1;
33073 }
33074
33075 pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks);
33076 if (pDevice->jack.ppPortsPlayback == NULL) {
33077 ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks);
33078 return MA_OUT_OF_MEMORY;
33079 }
33080
33081 for (iPort = 0; iPort < pDescriptorPlayback->channels; iPort += 1) {
33082 char name[64];
33083 ma_strcpy_s(name, sizeof(name), "playback");
33084 ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */
33085
33086 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);
33087 if (pDevice->jack.ppPortsPlayback[iPort] == NULL) {
33088 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
33089 ma_device_uninit__jack(pDevice);
33090 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.");
33092 }
33093 }
33094
33095 ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
33096
33097 pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
33098 pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */
33099
33100 pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks);
33101 if (pDevice->jack.pIntermediaryBufferPlayback == NULL) {
33102 ma_device_uninit__jack(pDevice);
33103 return MA_OUT_OF_MEMORY;
33104 }
33105 }
33106
33107 return MA_SUCCESS;
33108}
33109
33110
33111static ma_result ma_device_start__jack(ma_device* pDevice)
33112{
33113 ma_context* pContext = pDevice->pContext;
33114 int resultJACK;
33115 size_t i;
33116
33117 resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient);
33118 if (resultJACK != 0) {
33119 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client.");
33121 }
33122
33123 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
33124 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);
33125 if (ppServerPorts == NULL) {
33126 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
33127 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.");
33128 return MA_ERROR;
33129 }
33130
33131 for (i = 0; ppServerPorts[i] != NULL; ++i) {
33132 const char* pServerPort = ppServerPorts[i];
33133 const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]);
33134
33135 resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort);
33136 if (resultJACK != 0) {
33137 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
33138 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
33139 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.");
33140 return MA_ERROR;
33141 }
33142 }
33143
33144 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
33145 }
33146
33147 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
33148 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);
33149 if (ppServerPorts == NULL) {
33150 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
33151 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.");
33152 return MA_ERROR;
33153 }
33154
33155 for (i = 0; ppServerPorts[i] != NULL; ++i) {
33156 const char* pServerPort = ppServerPorts[i];
33157 const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]);
33158
33159 resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort);
33160 if (resultJACK != 0) {
33161 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
33162 ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
33163 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.");
33164 return MA_ERROR;
33165 }
33166 }
33167
33168 ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
33169 }
33170
33171 return MA_SUCCESS;
33172}
33173
33174static ma_result ma_device_stop__jack(ma_device* pDevice)
33175{
33176 ma_context* pContext = pDevice->pContext;
33177
33178 if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) {
33179 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.");
33180 return MA_ERROR;
33181 }
33182
33183 ma_device__on_notification_stopped(pDevice);
33184
33185 return MA_SUCCESS;
33186}
33187
33188
33189static ma_result ma_context_uninit__jack(ma_context* pContext)
33190{
33191 MA_ASSERT(pContext != NULL);
33192 MA_ASSERT(pContext->backend == ma_backend_jack);
33193
33194 ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
33195 pContext->jack.pClientName = NULL;
33196
33197#ifndef MA_NO_RUNTIME_LINKING
33198 ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO);
33199#endif
33200
33201 return MA_SUCCESS;
33202}
33203
33204static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
33205{
33206#ifndef MA_NO_RUNTIME_LINKING
33207 const char* libjackNames[] = {
33208#if defined(MA_WIN32)
33209 "libjack.dll",
33210 "libjack64.dll"
33211#endif
33212#if defined(MA_UNIX)
33213 "libjack.so",
33214 "libjack.so.0"
33215#endif
33216 };
33217 size_t i;
33218
33219 for (i = 0; i < ma_countof(libjackNames); ++i) {
33220 pContext->jack.jackSO = ma_dlopen(ma_context_get_log(pContext), libjackNames[i]);
33221 if (pContext->jack.jackSO != NULL) {
33222 break;
33223 }
33224 }
33225
33226 if (pContext->jack.jackSO == NULL) {
33227 return MA_NO_BACKEND;
33228 }
33229
33230 pContext->jack.jack_client_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_open");
33231 pContext->jack.jack_client_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_close");
33232 pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_name_size");
33233 pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_process_callback");
33234 pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_buffer_size_callback");
33235 pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_on_shutdown");
33236 pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_sample_rate");
33237 pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_buffer_size");
33238 pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_ports");
33239 pContext->jack.jack_activate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_activate");
33240 pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_deactivate");
33241 pContext->jack.jack_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_connect");
33242 pContext->jack.jack_port_register = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_register");
33243 pContext->jack.jack_port_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_name");
33244 pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_get_buffer");
33245 pContext->jack.jack_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_free");
33246#else
33247 /*
33248 This strange assignment system is here just to ensure type safety of miniaudio's function pointer
33249 types. If anything differs slightly the compiler should throw a warning.
33250 */
33251 ma_jack_client_open_proc _jack_client_open = jack_client_open;
33252 ma_jack_client_close_proc _jack_client_close = jack_client_close;
33253 ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size;
33254 ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback;
33255 ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback;
33256 ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown;
33257 ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate;
33258 ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size;
33259 ma_jack_get_ports_proc _jack_get_ports = jack_get_ports;
33260 ma_jack_activate_proc _jack_activate = jack_activate;
33261 ma_jack_deactivate_proc _jack_deactivate = jack_deactivate;
33262 ma_jack_connect_proc _jack_connect = jack_connect;
33263 ma_jack_port_register_proc _jack_port_register = jack_port_register;
33264 ma_jack_port_name_proc _jack_port_name = jack_port_name;
33265 ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer;
33266 ma_jack_free_proc _jack_free = jack_free;
33267
33268 pContext->jack.jack_client_open = (ma_proc)_jack_client_open;
33269 pContext->jack.jack_client_close = (ma_proc)_jack_client_close;
33270 pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size;
33271 pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback;
33272 pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback;
33273 pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown;
33274 pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate;
33275 pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size;
33276 pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports;
33277 pContext->jack.jack_activate = (ma_proc)_jack_activate;
33278 pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate;
33279 pContext->jack.jack_connect = (ma_proc)_jack_connect;
33280 pContext->jack.jack_port_register = (ma_proc)_jack_port_register;
33281 pContext->jack.jack_port_name = (ma_proc)_jack_port_name;
33282 pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer;
33283 pContext->jack.jack_free = (ma_proc)_jack_free;
33284#endif
33285
33286 if (pConfig->jack.pClientName != NULL) {
33287 pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks);
33288 }
33289 pContext->jack.tryStartServer = pConfig->jack.tryStartServer;
33290
33291 /*
33292 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
33293 a temporary client.
33294 */
33295 {
33296 ma_jack_client_t* pDummyClient;
33297 ma_result result = ma_context_open_client__jack(pContext, &pDummyClient);
33298 if (result != MA_SUCCESS) {
33299 ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
33300 #ifndef MA_NO_RUNTIME_LINKING
33301 ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO);
33302 #endif
33303 return MA_NO_BACKEND;
33304 }
33305
33306 ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient);
33307 }
33308
33309
33310 pCallbacks->onContextInit = ma_context_init__jack;
33311 pCallbacks->onContextUninit = ma_context_uninit__jack;
33312 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack;
33313 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack;
33314 pCallbacks->onDeviceInit = ma_device_init__jack;
33315 pCallbacks->onDeviceUninit = ma_device_uninit__jack;
33316 pCallbacks->onDeviceStart = ma_device_start__jack;
33317 pCallbacks->onDeviceStop = ma_device_stop__jack;
33318 pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */
33319 pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */
33320 pCallbacks->onDeviceDataLoop = NULL; /* Not used because JACK is asynchronous. */
33321
33322 return MA_SUCCESS;
33323}
33324#endif /* MA_HAS_JACK */
33325
33326
33327
33328/******************************************************************************
33329
33330Core Audio Backend
33331
33332References
33333==========
33334- Technical Note TN2091: Device input using the HAL Output Audio Unit
33335 https://developer.apple.com/library/archive/technotes/tn2091/_index.html
33336
33337******************************************************************************/
33338#ifdef MA_HAS_COREAUDIO
33339#include <TargetConditionals.h>
33340
33341#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1
33342 #define MA_APPLE_MOBILE
33343 #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
33344 #define MA_APPLE_TV
33345 #endif
33346 #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
33347 #define MA_APPLE_WATCH
33348 #endif
33349 #if __has_feature(objc_arc)
33350 #define MA_BRIDGE_TRANSFER __bridge_transfer
33351 #define MA_BRIDGE_RETAINED __bridge_retained
33352 #else
33353 #define MA_BRIDGE_TRANSFER
33354 #define MA_BRIDGE_RETAINED
33355 #endif
33356#else
33357 #define MA_APPLE_DESKTOP
33358#endif
33359
33360#if defined(MA_APPLE_DESKTOP)
33361#include <CoreAudio/CoreAudio.h>
33362#else
33363#include <AVFoundation/AVFoundation.h>
33364#endif
33365
33366#include <AudioToolbox/AudioToolbox.h>
33367
33368/* CoreFoundation */
33369typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding);
33370typedef void (* ma_CFRelease_proc)(CFTypeRef cf);
33371
33372/* CoreAudio */
33373#if defined(MA_APPLE_DESKTOP)
33374typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData);
33375typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);
33376typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
33377typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
33378typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
33379#endif
33380
33381/* AudioToolbox */
33382typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc);
33383typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance);
33384typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance);
33385typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit);
33386typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit);
33387typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData);
33388typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable);
33389typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize);
33390typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
33391typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit);
33392typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
33393
33394
33395#define MA_COREAUDIO_OUTPUT_BUS 0
33396#define MA_COREAUDIO_INPUT_BUS 1
33397
33398#if defined(MA_APPLE_DESKTOP)
33399static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit);
33400#endif
33401
33402/*
33403Core Audio
33404
33405So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation
33406apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose
33407needing to figure out how this darn thing works, I'm going to outline a few things here.
33408
33409Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be
33410able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen
33411that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent
33412and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the
33413distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API.
33414
33415Most (all?) functions in the AudioObject API take a AudioObjectID as its input. This is the device identifier. When
33416retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific
33417data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the
33418devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be
33419the central APIs for retrieving information about the system and specific devices.
33420
33421To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a
33422structure with three variables and is used to identify which property you are getting or setting. The first is the "selector"
33423which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is
33424typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and
33425kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to
33426kAudioObjectPropertyElementMain in miniaudio's case. I don't know of any cases where this would be set to anything different.
33427
33428Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size
33429of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property
33430address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the
33431size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of
33432AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count.
33433*/
33434
33435#if defined(MA_APPLE_MOBILE)
33436static void ma_device__on_notification_interruption_began(ma_device* pDevice)
33437{
33438 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began));
33439}
33440
33441static void ma_device__on_notification_interruption_ended(ma_device* pDevice)
33442{
33443 ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended));
33444}
33445#endif
33446
33447static ma_result ma_result_from_OSStatus(OSStatus status)
33448{
33449 switch (status)
33450 {
33451 case noErr: return MA_SUCCESS;
33452 #if defined(MA_APPLE_DESKTOP)
33453 case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED;
33454 case kAudioHardwareUnspecifiedError: return MA_ERROR;
33455 case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS;
33456 case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION;
33457 case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION;
33458 case kAudioHardwareBadObjectError: return MA_INVALID_ARGS;
33459 case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS;
33460 case kAudioHardwareBadStreamError: return MA_INVALID_ARGS;
33461 case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION;
33462 case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED;
33463 case kAudioDevicePermissionsError: return MA_ACCESS_DENIED;
33464 #endif
33465 default: return MA_ERROR;
33466 }
33467}
33468
33469#if 0
33470static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit)
33471{
33472 switch (bit)
33473 {
33474 case kAudioChannelBit_Left: return MA_CHANNEL_LEFT;
33475 case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT;
33476 case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER;
33477 case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE;
33478 case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT;
33479 case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT;
33480 case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
33481 case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
33482 case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER;
33483 case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
33484 case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
33485 case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
33486 case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
33487 case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
33488 case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
33489 case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
33490 case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
33491 case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
33492 default: return MA_CHANNEL_NONE;
33493 }
33494}
33495#endif
33496
33497static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut)
33498{
33499 MA_ASSERT(pDescription != NULL);
33500 MA_ASSERT(pFormatOut != NULL);
33501
33502 *pFormatOut = ma_format_unknown; /* Safety. */
33503
33504 /* There's a few things miniaudio doesn't support. */
33505 if (pDescription->mFormatID != kAudioFormatLinearPCM) {
33507 }
33508
33509 /* We don't support any non-packed formats that are aligned high. */
33510 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) {
33512 }
33513
33514 /* Only supporting native-endian. */
33515 if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) {
33517 }
33518
33519 /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */
33520 /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) {
33521 return MA_FORMAT_NOT_SUPPORTED;
33522 }*/
33523
33524 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) {
33525 if (pDescription->mBitsPerChannel == 32) {
33526 *pFormatOut = ma_format_f32;
33527 return MA_SUCCESS;
33528 }
33529 } else {
33530 if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) {
33531 if (pDescription->mBitsPerChannel == 16) {
33532 *pFormatOut = ma_format_s16;
33533 return MA_SUCCESS;
33534 } else if (pDescription->mBitsPerChannel == 24) {
33535 if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) {
33536 *pFormatOut = ma_format_s24;
33537 return MA_SUCCESS;
33538 } else {
33539 if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) {
33540 /* TODO: Implement ma_format_s24_32. */
33542 /*return MA_SUCCESS;*/
33544 }
33545 }
33546 } else if (pDescription->mBitsPerChannel == 32) {
33547 *pFormatOut = ma_format_s32;
33548 return MA_SUCCESS;
33549 }
33550 } else {
33551 if (pDescription->mBitsPerChannel == 8) {
33552 *pFormatOut = ma_format_u8;
33553 return MA_SUCCESS;
33554 }
33555 }
33556 }
33557
33558 /* Getting here means the format is not supported. */
33560}
33561
33562#if defined(MA_APPLE_DESKTOP)
33563static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label)
33564{
33565 switch (label)
33566 {
33567 case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE;
33568 case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE;
33569 case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE;
33570 case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT;
33571 case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT;
33572 case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER;
33573 case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE;
33574 case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT;
33575 case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT;
33576 case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
33577 case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
33578 case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER;
33579 case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
33580 case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
33581 case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
33582 case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
33583 case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
33584 case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
33585 case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
33586 case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
33587 case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
33588 case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT;
33589 case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT;
33590 case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT;
33591 case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT;
33592 case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE;
33593 case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT;
33594 case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT;
33595 case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE;
33596 case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO;
33597 case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO;
33598 case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO;
33599 case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER;
33600 case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE;
33601 case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE;
33602 case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE;
33603 case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE;
33604 case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE;
33605 case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT;
33606 case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT;
33607 case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT;
33608 case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT;
33609 case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT;
33610 case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT;
33611 case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE;
33612 case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE;
33613 case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE;
33614 case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0;
33615 case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1;
33616 case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2;
33617 case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3;
33618 case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4;
33619 case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5;
33620 case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6;
33621 case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7;
33622 case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8;
33623 case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9;
33624 case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10;
33625 case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11;
33626 case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12;
33627 case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13;
33628 case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14;
33629 case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15;
33630 case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE;
33631
33632 #if 0 /* Introduced in a later version of macOS. */
33633 case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE;
33634 case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0;
33635 case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1;
33636 case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2;
33637 case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3;
33638 case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4;
33639 case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5;
33640 case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6;
33641 case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7;
33642 case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8;
33643 case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9;
33644 case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10;
33645 case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11;
33646 case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12;
33647 case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13;
33648 case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14;
33649 case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15;
33650 case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE;
33651 #endif
33652
33653 default: return MA_CHANNEL_NONE;
33654 }
33655}
33656
33657static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap)
33658{
33659 MA_ASSERT(pChannelLayout != NULL);
33660
33661 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
33662 UInt32 iChannel;
33663 for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) {
33664 pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);
33665 }
33666 } else
33667#if 0
33668 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
33669 /* This is the same kind of system that's used by Windows audio APIs. */
33670 UInt32 iChannel = 0;
33671 UInt32 iBit;
33672 AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap;
33673 for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) {
33674 AudioChannelBitmap bit = bitmap & (1 << iBit);
33675 if (bit != 0) {
33676 pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit);
33677 }
33678 }
33679 } else
33680#endif
33681 {
33682 /*
33683 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
33684 be updated to determine the mapping based on the tag.
33685 */
33686 UInt32 channelCount;
33687
33688 /* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */
33689 if (channelMapCap > 0xFFFFFFFF) {
33690 channelMapCap = 0xFFFFFFFF;
33691 }
33692
33693 channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap);
33694
33695 switch (pChannelLayout->mChannelLayoutTag)
33696 {
33697 case kAudioChannelLayoutTag_Mono:
33698 case kAudioChannelLayoutTag_Stereo:
33699 case kAudioChannelLayoutTag_StereoHeadphones:
33700 case kAudioChannelLayoutTag_MatrixStereo:
33701 case kAudioChannelLayoutTag_MidSide:
33702 case kAudioChannelLayoutTag_XY:
33703 case kAudioChannelLayoutTag_Binaural:
33704 case kAudioChannelLayoutTag_Ambisonic_B_Format:
33705 {
33706 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
33707 } break;
33708
33709 case kAudioChannelLayoutTag_Octagonal:
33710 {
33711 pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
33712 pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
33713 } MA_FALLTHROUGH; /* Intentional fallthrough. */
33714 case kAudioChannelLayoutTag_Hexagonal:
33715 {
33716 pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
33717 } MA_FALLTHROUGH; /* Intentional fallthrough. */
33718 case kAudioChannelLayoutTag_Pentagonal:
33719 {
33720 pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
33721 } MA_FALLTHROUGH; /* Intentional fallthrough. */
33722 case kAudioChannelLayoutTag_Quadraphonic:
33723 {
33724 pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
33725 pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
33726 pChannelMap[1] = MA_CHANNEL_RIGHT;
33727 pChannelMap[0] = MA_CHANNEL_LEFT;
33728 } break;
33729
33730 /* TODO: Add support for more tags here. */
33731
33732 default:
33733 {
33734 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
33735 } break;
33736 }
33737 }
33738
33739 return MA_SUCCESS;
33740}
33741
33742#if (defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) || \
33743 (defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0)
33744#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMain
33745#else
33746/* kAudioObjectPropertyElementMaster is deprecated. */
33747#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster
33748#endif
33749
33750/* kAudioDevicePropertyScope* were renamed to kAudioObjectPropertyScope* in 10.8. */
33751#if !defined(MAC_OS_X_VERSION_10_8) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8)
33752#define kAudioObjectPropertyScopeInput kAudioDevicePropertyScopeInput
33753#define kAudioObjectPropertyScopeOutput kAudioDevicePropertyScopeOutput
33754#endif
33755
33756static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */
33757{
33758 AudioObjectPropertyAddress propAddressDevices;
33759 UInt32 deviceObjectsDataSize;
33760 OSStatus status;
33761 AudioObjectID* pDeviceObjectIDs;
33762
33763 MA_ASSERT(pContext != NULL);
33764 MA_ASSERT(pDeviceCount != NULL);
33765 MA_ASSERT(ppDeviceObjectIDs != NULL);
33766
33767 /* Safety. */
33768 *pDeviceCount = 0;
33769 *ppDeviceObjectIDs = NULL;
33770
33771 propAddressDevices.mSelector = kAudioHardwarePropertyDevices;
33772 propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal;
33773 propAddressDevices.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
33774
33775 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize);
33776 if (status != noErr) {
33777 return ma_result_from_OSStatus(status);
33778 }
33779
33780 pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks);
33781 if (pDeviceObjectIDs == NULL) {
33782 return MA_OUT_OF_MEMORY;
33783 }
33784
33785 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs);
33786 if (status != noErr) {
33787 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
33788 return ma_result_from_OSStatus(status);
33789 }
33790
33791 *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID);
33792 *ppDeviceObjectIDs = pDeviceObjectIDs;
33793
33794 return MA_SUCCESS;
33795}
33796
33797static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID)
33798{
33799 AudioObjectPropertyAddress propAddress;
33800 UInt32 dataSize;
33801 OSStatus status;
33802
33803 MA_ASSERT(pContext != NULL);
33804
33805 propAddress.mSelector = kAudioDevicePropertyDeviceUID;
33806 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
33807 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
33808
33809 dataSize = sizeof(*pUID);
33810 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID);
33811 if (status != noErr) {
33812 return ma_result_from_OSStatus(status);
33813 }
33814
33815 return MA_SUCCESS;
33816}
33817
33818static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
33819{
33820 CFStringRef uid;
33821 ma_result result;
33822
33823 MA_ASSERT(pContext != NULL);
33824
33825 result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid);
33826 if (result != MA_SUCCESS) {
33827 return result;
33828 }
33829
33830 if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
33831 return MA_ERROR;
33832 }
33833
33834 ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid);
33835 return MA_SUCCESS;
33836}
33837
33838static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
33839{
33840 AudioObjectPropertyAddress propAddress;
33841 CFStringRef deviceName = NULL;
33842 UInt32 dataSize;
33843 OSStatus status;
33844
33845 MA_ASSERT(pContext != NULL);
33846
33847 propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
33848 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
33849 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
33850
33851 dataSize = sizeof(deviceName);
33852 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName);
33853 if (status != noErr) {
33854 return ma_result_from_OSStatus(status);
33855 }
33856
33857 if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
33858 return MA_ERROR;
33859 }
33860
33861 ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName);
33862 return MA_SUCCESS;
33863}
33864
33865static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope)
33866{
33867 AudioObjectPropertyAddress propAddress;
33868 UInt32 dataSize;
33869 OSStatus status;
33870 AudioBufferList* pBufferList;
33871 ma_bool32 isSupported;
33872
33873 MA_ASSERT(pContext != NULL);
33874
33875 /* 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. */
33876 propAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
33877 propAddress.mScope = scope;
33878 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
33879
33880 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
33881 if (status != noErr) {
33882 return MA_FALSE;
33883 }
33884
33885 pBufferList = (AudioBufferList*)ma_malloc(dataSize, &pContext->allocationCallbacks);
33886 if (pBufferList == NULL) {
33887 return MA_FALSE; /* Out of memory. */
33888 }
33889
33890 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList);
33891 if (status != noErr) {
33892 ma_free(pBufferList, &pContext->allocationCallbacks);
33893 return MA_FALSE;
33894 }
33895
33896 isSupported = MA_FALSE;
33897 if (pBufferList->mNumberBuffers > 0) {
33898 isSupported = MA_TRUE;
33899 }
33900
33901 ma_free(pBufferList, &pContext->allocationCallbacks);
33902 return isSupported;
33903}
33904
33905static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID)
33906{
33907 return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput);
33908}
33909
33910static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID)
33911{
33912 return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput);
33913}
33914
33915
33916static 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(). */
33917{
33918 AudioObjectPropertyAddress propAddress;
33919 UInt32 dataSize;
33920 OSStatus status;
33921 AudioStreamRangedDescription* pDescriptions;
33922
33923 MA_ASSERT(pContext != NULL);
33924 MA_ASSERT(pDescriptionCount != NULL);
33925 MA_ASSERT(ppDescriptions != NULL);
33926
33927 /*
33928 TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My
33929 MacBook Pro uses s24/32 format, however, which miniaudio does not currently support.
33930 */
33931 propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/
33932 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
33933 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
33934
33935 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
33936 if (status != noErr) {
33937 return ma_result_from_OSStatus(status);
33938 }
33939
33940 pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks);
33941 if (pDescriptions == NULL) {
33942 return MA_OUT_OF_MEMORY;
33943 }
33944
33945 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions);
33946 if (status != noErr) {
33947 ma_free(pDescriptions, &pContext->allocationCallbacks);
33948 return ma_result_from_OSStatus(status);
33949 }
33950
33951 *pDescriptionCount = dataSize / sizeof(*pDescriptions);
33952 *ppDescriptions = pDescriptions;
33953 return MA_SUCCESS;
33954}
33955
33956
33957static 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(). */
33958{
33959 AudioObjectPropertyAddress propAddress;
33960 UInt32 dataSize;
33961 OSStatus status;
33962 AudioChannelLayout* pChannelLayout;
33963
33964 MA_ASSERT(pContext != NULL);
33965 MA_ASSERT(ppChannelLayout != NULL);
33966
33967 *ppChannelLayout = NULL; /* Safety. */
33968
33969 propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
33970 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
33971 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
33972
33973 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
33974 if (status != noErr) {
33975 return ma_result_from_OSStatus(status);
33976 }
33977
33978 pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks);
33979 if (pChannelLayout == NULL) {
33980 return MA_OUT_OF_MEMORY;
33981 }
33982
33983 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout);
33984 if (status != noErr) {
33985 ma_free(pChannelLayout, &pContext->allocationCallbacks);
33986 return ma_result_from_OSStatus(status);
33987 }
33988
33989 *ppChannelLayout = pChannelLayout;
33990 return MA_SUCCESS;
33991}
33992
33993static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount)
33994{
33995 AudioChannelLayout* pChannelLayout;
33996 ma_result result;
33997
33998 MA_ASSERT(pContext != NULL);
33999 MA_ASSERT(pChannelCount != NULL);
34000
34001 *pChannelCount = 0; /* Safety. */
34002
34003 result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
34004 if (result != MA_SUCCESS) {
34005 return result;
34006 }
34007
34008 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
34009 *pChannelCount = pChannelLayout->mNumberChannelDescriptions;
34010 } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
34011 *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap);
34012 } else {
34013 *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
34014 }
34015
34016 ma_free(pChannelLayout, &pContext->allocationCallbacks);
34017 return MA_SUCCESS;
34018}
34019
34020#if 0
34021static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
34022{
34023 AudioChannelLayout* pChannelLayout;
34024 ma_result result;
34025
34026 MA_ASSERT(pContext != NULL);
34027
34028 result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
34029 if (result != MA_SUCCESS) {
34030 return result; /* Rather than always failing here, would it be more robust to simply assume a default? */
34031 }
34032
34033 result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
34034 if (result != MA_SUCCESS) {
34035 ma_free(pChannelLayout, &pContext->allocationCallbacks);
34036 return result;
34037 }
34038
34039 ma_free(pChannelLayout, &pContext->allocationCallbacks);
34040 return result;
34041}
34042#endif
34043
34044static 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(). */
34045{
34046 AudioObjectPropertyAddress propAddress;
34047 UInt32 dataSize;
34048 OSStatus status;
34049 AudioValueRange* pSampleRateRanges;
34050
34051 MA_ASSERT(pContext != NULL);
34052 MA_ASSERT(pSampleRateRangesCount != NULL);
34053 MA_ASSERT(ppSampleRateRanges != NULL);
34054
34055 /* Safety. */
34056 *pSampleRateRangesCount = 0;
34057 *ppSampleRateRanges = NULL;
34058
34059 propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
34060 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
34061 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
34062
34063 status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
34064 if (status != noErr) {
34065 return ma_result_from_OSStatus(status);
34066 }
34067
34068 pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks);
34069 if (pSampleRateRanges == NULL) {
34070 return MA_OUT_OF_MEMORY;
34071 }
34072
34073 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges);
34074 if (status != noErr) {
34075 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
34076 return ma_result_from_OSStatus(status);
34077 }
34078
34079 *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges);
34080 *ppSampleRateRanges = pSampleRateRanges;
34081 return MA_SUCCESS;
34082}
34083
34084#if 0
34085static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut)
34086{
34087 UInt32 sampleRateRangeCount;
34088 AudioValueRange* pSampleRateRanges;
34089 ma_result result;
34090
34091 MA_ASSERT(pContext != NULL);
34092 MA_ASSERT(pSampleRateOut != NULL);
34093
34094 *pSampleRateOut = 0; /* Safety. */
34095
34096 result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
34097 if (result != MA_SUCCESS) {
34098 return result;
34099 }
34100
34101 if (sampleRateRangeCount == 0) {
34102 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
34103 return MA_ERROR; /* Should never hit this case should we? */
34104 }
34105
34106 if (sampleRateIn == 0) {
34107 /* Search in order of miniaudio's preferred priority. */
34108 UInt32 iMALSampleRate;
34109 for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) {
34110 ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate];
34111 UInt32 iCASampleRate;
34112 for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) {
34113 AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate];
34114 if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) {
34115 *pSampleRateOut = malSampleRate;
34116 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
34117 return MA_SUCCESS;
34118 }
34119 }
34120 }
34121
34122 /*
34123 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
34124 case we just fall back to the first one reported by Core Audio.
34125 */
34126 MA_ASSERT(sampleRateRangeCount > 0);
34127
34128 *pSampleRateOut = pSampleRateRanges[0].mMinimum;
34129 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
34130 return MA_SUCCESS;
34131 } else {
34132 /* Find the closest match to this sample rate. */
34133 UInt32 currentAbsoluteDifference = INT32_MAX;
34134 UInt32 iCurrentClosestRange = (UInt32)-1;
34135 UInt32 iRange;
34136 for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) {
34137 if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) {
34138 *pSampleRateOut = sampleRateIn;
34139 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
34140 return MA_SUCCESS;
34141 } else {
34142 UInt32 absoluteDifference;
34143 if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) {
34144 absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn;
34145 } else {
34146 absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum;
34147 }
34148
34149 if (currentAbsoluteDifference > absoluteDifference) {
34150 currentAbsoluteDifference = absoluteDifference;
34151 iCurrentClosestRange = iRange;
34152 }
34153 }
34154 }
34155
34156 MA_ASSERT(iCurrentClosestRange != (UInt32)-1);
34157
34158 *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum;
34159 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
34160 return MA_SUCCESS;
34161 }
34162
34163 /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */
34164 /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/
34165 /*return MA_ERROR;*/
34166}
34167#endif
34168
34169static 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)
34170{
34171 AudioObjectPropertyAddress propAddress;
34172 AudioValueRange bufferSizeRange;
34173 UInt32 dataSize;
34174 OSStatus status;
34175
34176 MA_ASSERT(pContext != NULL);
34177 MA_ASSERT(pBufferSizeInFramesOut != NULL);
34178
34179 *pBufferSizeInFramesOut = 0; /* Safety. */
34180
34181 propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
34182 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
34183 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
34184
34185 dataSize = sizeof(bufferSizeRange);
34186 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange);
34187 if (status != noErr) {
34188 return ma_result_from_OSStatus(status);
34189 }
34190
34191 /* This is just a clamp. */
34192 if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) {
34193 *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum;
34194 } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) {
34195 *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum;
34196 } else {
34197 *pBufferSizeInFramesOut = bufferSizeInFramesIn;
34198 }
34199
34200 return MA_SUCCESS;
34201}
34202
34203static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut)
34204{
34205 ma_result result;
34206 ma_uint32 chosenBufferSizeInFrames;
34207 AudioObjectPropertyAddress propAddress;
34208 UInt32 dataSize;
34209 OSStatus status;
34210
34211 MA_ASSERT(pContext != NULL);
34212
34213 result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames);
34214 if (result != MA_SUCCESS) {
34215 return result;
34216 }
34217
34218 /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */
34219 propAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
34220 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
34221 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
34222
34223 ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames);
34224
34225 /* Get the actual size of the buffer. */
34226 dataSize = sizeof(*pPeriodSizeInOut);
34227 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames);
34228 if (status != noErr) {
34229 return ma_result_from_OSStatus(status);
34230 }
34231
34232 *pPeriodSizeInOut = chosenBufferSizeInFrames;
34233 return MA_SUCCESS;
34234}
34235
34236static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID)
34237{
34238 AudioObjectPropertyAddress propAddressDefaultDevice;
34239 UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
34240 AudioObjectID defaultDeviceObjectID;
34241 OSStatus status;
34242
34243 MA_ASSERT(pContext != NULL);
34244 MA_ASSERT(pDeviceObjectID != NULL);
34245
34246 /* Safety. */
34247 *pDeviceObjectID = 0;
34248
34249 propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal;
34250 propAddressDefaultDevice.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
34251 if (deviceType == ma_device_type_playback) {
34252 propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
34253 } else {
34254 propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice;
34255 }
34256
34257 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
34258 status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID);
34259 if (status == noErr) {
34260 *pDeviceObjectID = defaultDeviceObjectID;
34261 return MA_SUCCESS;
34262 }
34263
34264 /* If we get here it means we couldn't find the device. */
34265 return MA_NO_DEVICE;
34266}
34267
34268static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID)
34269{
34270 MA_ASSERT(pContext != NULL);
34271 MA_ASSERT(pDeviceObjectID != NULL);
34272
34273 /* Safety. */
34274 *pDeviceObjectID = 0;
34275
34276 if (pDeviceID == NULL) {
34277 /* Default device. */
34278 return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID);
34279 } else {
34280 /* Explicit device. */
34281 UInt32 deviceCount;
34282 AudioObjectID* pDeviceObjectIDs;
34283 ma_result result;
34284 UInt32 iDevice;
34285
34286 result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
34287 if (result != MA_SUCCESS) {
34288 return result;
34289 }
34290
34291 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
34292 AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
34293
34294 char uid[256];
34295 if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) {
34296 continue;
34297 }
34298
34299 if (deviceType == ma_device_type_playback) {
34300 if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
34301 if (strcmp(uid, pDeviceID->coreaudio) == 0) {
34302 *pDeviceObjectID = deviceObjectID;
34303 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
34304 return MA_SUCCESS;
34305 }
34306 }
34307 } else {
34308 if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
34309 if (strcmp(uid, pDeviceID->coreaudio) == 0) {
34310 *pDeviceObjectID = deviceObjectID;
34311 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
34312 return MA_SUCCESS;
34313 }
34314 }
34315 }
34316 }
34317
34318 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
34319 }
34320
34321 /* If we get here it means we couldn't find the device. */
34322 return MA_NO_DEVICE;
34323}
34324
34325
34326static 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)
34327{
34328 UInt32 deviceFormatDescriptionCount;
34329 AudioStreamRangedDescription* pDeviceFormatDescriptions;
34330 ma_result result;
34331 ma_uint32 desiredSampleRate;
34332 ma_uint32 desiredChannelCount;
34333 ma_format desiredFormat;
34334 AudioStreamBasicDescription bestDeviceFormatSoFar;
34335 ma_bool32 hasSupportedFormat;
34336 UInt32 iFormat;
34337
34338 result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions);
34339 if (result != MA_SUCCESS) {
34340 return result;
34341 }
34342
34343 desiredSampleRate = sampleRate;
34344 if (desiredSampleRate == 0) {
34345 desiredSampleRate = (ma_uint32)pOrigFormat->mSampleRate;
34346 }
34347
34348 desiredChannelCount = channels;
34349 if (desiredChannelCount == 0) {
34350 desiredChannelCount = pOrigFormat->mChannelsPerFrame;
34351 }
34352
34353 desiredFormat = format;
34354 if (desiredFormat == ma_format_unknown) {
34355 result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat);
34356 if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) {
34357 desiredFormat = g_maFormatPriorities[0];
34358 }
34359 }
34360
34361 /*
34362 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
34363 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.
34364 */
34365 MA_ZERO_OBJECT(&bestDeviceFormatSoFar);
34366
34367 hasSupportedFormat = MA_FALSE;
34368 for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
34369 ma_format formatFromDescription;
34370 ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &formatFromDescription);
34371 if (formatResult == MA_SUCCESS && formatFromDescription != ma_format_unknown) {
34372 hasSupportedFormat = MA_TRUE;
34373 bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat;
34374 break;
34375 }
34376 }
34377
34378 if (!hasSupportedFormat) {
34379 ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
34381 }
34382
34383
34384 for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
34385 AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat;
34386 ma_format thisSampleFormat;
34387 ma_result formatResult;
34388 ma_format bestSampleFormatSoFar;
34389
34390 /* If the format is not supported by miniaudio we need to skip this one entirely. */
34391 formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat);
34392 if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) {
34393 continue; /* The format is not supported by miniaudio. Skip. */
34394 }
34395
34396 ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar);
34397
34398 /* Getting here means the format is supported by miniaudio which makes this format a candidate. */
34399 if (thisDeviceFormat.mSampleRate != desiredSampleRate) {
34400 /*
34401 The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format
34402 so far has an equal sample rate we can just ignore this one.
34403 */
34404 if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) {
34405 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. */
34406 } else {
34407 /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */
34408 if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) {
34409 /* This format has a different sample rate _and_ a different channel count. */
34410 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
34411 continue; /* No change to the best format. */
34412 } else {
34413 /*
34414 Both this format and the best so far have different sample rates and different channel counts. Whichever has the
34415 best format is the new best.
34416 */
34417 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
34418 bestDeviceFormatSoFar = thisDeviceFormat;
34419 continue;
34420 } else {
34421 continue; /* No change to the best format. */
34422 }
34423 }
34424 } else {
34425 /* This format has a different sample rate but the desired channel count. */
34426 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
34427 /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */
34428 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
34429 bestDeviceFormatSoFar = thisDeviceFormat;
34430 continue;
34431 } else {
34432 continue; /* No change to the best format for now. */
34433 }
34434 } else {
34435 /* This format has the desired channel count, but the best so far does not. We have a new best. */
34436 bestDeviceFormatSoFar = thisDeviceFormat;
34437 continue;
34438 }
34439 }
34440 }
34441 } else {
34442 /*
34443 The sample rates match which makes this format a very high priority contender. If the best format so far has a different
34444 sample rate it needs to be replaced with this one.
34445 */
34446 if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) {
34447 bestDeviceFormatSoFar = thisDeviceFormat;
34448 continue;
34449 } else {
34450 /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */
34451 if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) {
34452 /*
34453 In this case this format has the same channel count as what the client is requesting. If the best format so far has
34454 a different count, this one becomes the new best.
34455 */
34456 if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) {
34457 bestDeviceFormatSoFar = thisDeviceFormat;
34458 continue;
34459 } else {
34460 /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */
34461 if (thisSampleFormat == desiredFormat) {
34462 bestDeviceFormatSoFar = thisDeviceFormat;
34463 break; /* Found the exact match. */
34464 } else {
34465 /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */
34466 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
34467 bestDeviceFormatSoFar = thisDeviceFormat;
34468 continue;
34469 } else {
34470 continue; /* No change to the best format for now. */
34471 }
34472 }
34473 }
34474 } else {
34475 /*
34476 In this case the channel count is different to what the client has requested. If the best so far has the same channel
34477 count as the requested count then it remains the best.
34478 */
34479 if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
34480 continue;
34481 } else {
34482 /*
34483 This is the case where both have the same sample rate (good) but different channel counts. Right now both have about
34484 the same priority, but we need to compare the format now.
34485 */
34486 if (thisSampleFormat == bestSampleFormatSoFar) {
34487 if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
34488 bestDeviceFormatSoFar = thisDeviceFormat;
34489 continue;
34490 } else {
34491 continue; /* No change to the best format for now. */
34492 }
34493 }
34494 }
34495 }
34496 }
34497 }
34498 }
34499
34500 *pFormat = bestDeviceFormatSoFar;
34501
34502 ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
34503 return MA_SUCCESS;
34504}
34505
34506static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
34507{
34508 AudioUnitScope deviceScope;
34509 AudioUnitElement deviceBus;
34510 UInt32 channelLayoutSize;
34511 OSStatus status;
34512 AudioChannelLayout* pChannelLayout;
34513 ma_result result;
34514
34515 MA_ASSERT(pContext != NULL);
34516
34517 if (deviceType == ma_device_type_playback) {
34518 deviceScope = kAudioUnitScope_Input;
34519 deviceBus = MA_COREAUDIO_OUTPUT_BUS;
34520 } else {
34521 deviceScope = kAudioUnitScope_Output;
34522 deviceBus = MA_COREAUDIO_INPUT_BUS;
34523 }
34524
34525 status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL);
34526 if (status != noErr) {
34527 return ma_result_from_OSStatus(status);
34528 }
34529
34530 pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks);
34531 if (pChannelLayout == NULL) {
34532 return MA_OUT_OF_MEMORY;
34533 }
34534
34535 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize);
34536 if (status != noErr) {
34537 ma_free(pChannelLayout, &pContext->allocationCallbacks);
34538 return ma_result_from_OSStatus(status);
34539 }
34540
34541 result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
34542 if (result != MA_SUCCESS) {
34543 ma_free(pChannelLayout, &pContext->allocationCallbacks);
34544 return result;
34545 }
34546
34547 ma_free(pChannelLayout, &pContext->allocationCallbacks);
34548 return MA_SUCCESS;
34549}
34550#endif /* MA_APPLE_DESKTOP */
34551
34552
34553#if !defined(MA_APPLE_DESKTOP)
34554static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo)
34555{
34556 MA_ZERO_OBJECT(pInfo);
34557 ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1);
34558 ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1);
34559}
34560#endif
34561
34562static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
34563{
34564#if defined(MA_APPLE_DESKTOP)
34565 UInt32 deviceCount;
34566 AudioObjectID* pDeviceObjectIDs;
34567 AudioObjectID defaultDeviceObjectIDPlayback;
34568 AudioObjectID defaultDeviceObjectIDCapture;
34569 ma_result result;
34570 UInt32 iDevice;
34571
34572 ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */
34573 ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */
34574
34575 result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
34576 if (result != MA_SUCCESS) {
34577 return result;
34578 }
34579
34580 for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
34581 AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
34583
34584 MA_ZERO_OBJECT(&info);
34585 if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) {
34586 continue;
34587 }
34588 if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) {
34589 continue;
34590 }
34591
34592 if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
34593 if (deviceObjectID == defaultDeviceObjectIDPlayback) {
34594 info.isDefault = MA_TRUE;
34595 }
34596
34597 if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
34598 break;
34599 }
34600 }
34601 if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
34602 if (deviceObjectID == defaultDeviceObjectIDCapture) {
34603 info.isDefault = MA_TRUE;
34604 }
34605
34606 if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
34607 break;
34608 }
34609 }
34610 }
34611
34612 ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
34613#else
34615 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
34616 NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
34617
34618 for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
34619 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
34620 if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
34621 return MA_SUCCESS;
34622 }
34623 }
34624
34625 for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
34626 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
34627 if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
34628 return MA_SUCCESS;
34629 }
34630 }
34631#endif
34632
34633 return MA_SUCCESS;
34634}
34635
34636static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
34637{
34638 ma_result result;
34639
34640 MA_ASSERT(pContext != NULL);
34641
34642#if defined(MA_APPLE_DESKTOP)
34643 /* Desktop */
34644 {
34645 AudioObjectID deviceObjectID;
34646 AudioObjectID defaultDeviceObjectID;
34647 UInt32 streamDescriptionCount;
34648 AudioStreamRangedDescription* pStreamDescriptions;
34649 UInt32 iStreamDescription;
34650 UInt32 sampleRateRangeCount;
34651 AudioValueRange* pSampleRateRanges;
34652
34653 ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */
34654
34655 result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
34656 if (result != MA_SUCCESS) {
34657 return result;
34658 }
34659
34660 result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio);
34661 if (result != MA_SUCCESS) {
34662 return result;
34663 }
34664
34665 result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name);
34666 if (result != MA_SUCCESS) {
34667 return result;
34668 }
34669
34670 if (deviceObjectID == defaultDeviceObjectID) {
34671 pDeviceInfo->isDefault = MA_TRUE;
34672 }
34673
34674 /*
34675 There could be a large number of permutations here. Fortunately there is only a single channel count
34676 being reported which reduces this quite a bit. For sample rates we're only reporting those that are
34677 one of miniaudio's recognized "standard" rates. If there are still more formats than can fit into
34678 our fixed sized array we'll just need to truncate them. This is unlikely and will probably only happen
34679 if some driver performs software data conversion and therefore reports every possible format and
34680 sample rate.
34681 */
34682 pDeviceInfo->nativeDataFormatCount = 0;
34683
34684 /* Formats. */
34685 {
34686 ma_format uniqueFormats[ma_format_count];
34687 ma_uint32 uniqueFormatCount = 0;
34688 ma_uint32 channels;
34689
34690 /* Channels. */
34691 result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels);
34692 if (result != MA_SUCCESS) {
34693 return result;
34694 }
34695
34696 /* Formats. */
34697 result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);
34698 if (result != MA_SUCCESS) {
34699 return result;
34700 }
34701
34702 for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) {
34703 ma_format format;
34704 ma_bool32 hasFormatBeenHandled = MA_FALSE;
34705 ma_uint32 iOutputFormat;
34706 ma_uint32 iSampleRate;
34707
34708 result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format);
34709 if (result != MA_SUCCESS) {
34710 continue;
34711 }
34712
34713 MA_ASSERT(format != ma_format_unknown);
34714
34715 /* Make sure the format isn't already in the output list. */
34716 for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) {
34717 if (uniqueFormats[iOutputFormat] == format) {
34718 hasFormatBeenHandled = MA_TRUE;
34719 break;
34720 }
34721 }
34722
34723 /* If we've already handled this format just skip it. */
34724 if (hasFormatBeenHandled) {
34725 continue;
34726 }
34727
34728 uniqueFormats[uniqueFormatCount] = format;
34729 uniqueFormatCount += 1;
34730
34731 /* Sample Rates */
34732 result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
34733 if (result != MA_SUCCESS) {
34734 return result;
34735 }
34736
34737 /*
34738 Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are
34739 between this range.
34740 */
34741 for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) {
34742 ma_uint32 iStandardSampleRate;
34743 for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {
34744 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];
34745 if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) {
34746 /* We have a new data format. Add it to the list. */
34747 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
34748 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
34749 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate;
34750 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
34751 pDeviceInfo->nativeDataFormatCount += 1;
34752
34753 if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
34754 break; /* No more room for any more formats. */
34755 }
34756 }
34757 }
34758 }
34759
34760 ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
34761
34762 if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
34763 break; /* No more room for any more formats. */
34764 }
34765 }
34766
34767 ma_free(pStreamDescriptions, &pContext->allocationCallbacks);
34768 }
34769 }
34770#else
34771 /* Mobile */
34772 {
34773 AudioComponentDescription desc;
34774 AudioComponent component;
34775 AudioUnit audioUnit;
34776 OSStatus status;
34777 AudioUnitScope formatScope;
34778 AudioUnitElement formatElement;
34779 AudioStreamBasicDescription bestFormat;
34780 UInt32 propSize;
34781
34782 /* We want to ensure we use a consistent device name to device enumeration. */
34783 if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') {
34784 ma_bool32 found = MA_FALSE;
34785 if (deviceType == ma_device_type_playback) {
34786 NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
34787 for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
34788 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
34789 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
34790 found = MA_TRUE;
34791 break;
34792 }
34793 }
34794 } else {
34795 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
34796 for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
34797 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
34798 ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
34799 found = MA_TRUE;
34800 break;
34801 }
34802 }
34803 }
34804
34805 if (!found) {
34806 return MA_DOES_NOT_EXIST;
34807 }
34808 } else {
34809 if (deviceType == ma_device_type_playback) {
34810 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
34811 } else {
34812 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
34813 }
34814 }
34815
34816
34817 /*
34818 Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is
34819 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
34820 retrieve from the AVAudioSession shared instance.
34821 */
34822 desc.componentType = kAudioUnitType_Output;
34823 desc.componentSubType = kAudioUnitSubType_RemoteIO;
34824 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
34825 desc.componentFlags = 0;
34826 desc.componentFlagsMask = 0;
34827
34828 component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
34829 if (component == NULL) {
34831 }
34832
34833 status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit);
34834 if (status != noErr) {
34835 return ma_result_from_OSStatus(status);
34836 }
34837
34838 formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
34839 formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
34840
34841 propSize = sizeof(bestFormat);
34842 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
34843 if (status != noErr) {
34844 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
34845 return ma_result_from_OSStatus(status);
34846 }
34847
34848 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
34849 audioUnit = NULL;
34850
34851 /* Only a single format is being reported for iOS. */
34852 pDeviceInfo->nativeDataFormatCount = 1;
34853
34854 result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format);
34855 if (result != MA_SUCCESS) {
34856 return result;
34857 }
34858
34859 pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame;
34860
34861 /*
34862 It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do
34863 this we just get the shared instance and inspect.
34864 */
34865 @autoreleasepool {
34866 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
34867 MA_ASSERT(pAudioSession != NULL);
34868
34869 pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate;
34870 }
34871 }
34872#endif
34873
34874 (void)pDeviceInfo; /* Unused. */
34875 return MA_SUCCESS;
34876}
34877
34878static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks)
34879{
34880 AudioBufferList* pBufferList;
34881 UInt32 audioBufferSizeInBytes;
34882 size_t allocationSize;
34883
34884 MA_ASSERT(sizeInFrames > 0);
34885 MA_ASSERT(format != ma_format_unknown);
34886 MA_ASSERT(channels > 0);
34887
34888 allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */
34889 if (layout == ma_stream_layout_interleaved) {
34890 /* Interleaved case. This is the simple case because we just have one buffer. */
34891 allocationSize += sizeof(AudioBuffer) * 1;
34892 } else {
34893 /* Non-interleaved case. This is the more complex case because there's more than one buffer. */
34894 allocationSize += sizeof(AudioBuffer) * channels;
34895 }
34896
34897 allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels);
34898
34899 pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks);
34900 if (pBufferList == NULL) {
34901 return NULL;
34902 }
34903
34904 audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format));
34905
34906 if (layout == ma_stream_layout_interleaved) {
34907 pBufferList->mNumberBuffers = 1;
34908 pBufferList->mBuffers[0].mNumberChannels = channels;
34909 pBufferList->mBuffers[0].mDataByteSize = audioBufferSizeInBytes * channels;
34910 pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList);
34911 } else {
34912 ma_uint32 iBuffer;
34913 pBufferList->mNumberBuffers = channels;
34914 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
34915 pBufferList->mBuffers[iBuffer].mNumberChannels = 1;
34916 pBufferList->mBuffers[iBuffer].mDataByteSize = audioBufferSizeInBytes;
34917 pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer);
34918 }
34919 }
34920
34921 return pBufferList;
34922}
34923
34924static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout)
34925{
34926 MA_ASSERT(pDevice != NULL);
34927 MA_ASSERT(format != ma_format_unknown);
34928 MA_ASSERT(channels > 0);
34929
34930 /* Only resize the buffer if necessary. */
34931 if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) {
34932 AudioBufferList* pNewAudioBufferList;
34933
34934 pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks);
34935 if (pNewAudioBufferList == NULL) {
34936 return MA_OUT_OF_MEMORY;
34937 }
34938
34939 /* At this point we'll have a new AudioBufferList and we can free the old one. */
34940 ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
34941 pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList;
34942 pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames;
34943 }
34944
34945 /* Getting here means the capacity of the audio is fine. */
34946 return MA_SUCCESS;
34947}
34948
34949
34950static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList)
34951{
34952 ma_device* pDevice = (ma_device*)pUserData;
34953 ma_stream_layout layout;
34954
34955 MA_ASSERT(pDevice != NULL);
34956
34957 /*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);*/
34958
34959 /* 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. */
34961 if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) {
34963 }
34964
34965 if (layout == ma_stream_layout_interleaved) {
34966 /* For now we can assume everything is interleaved. */
34967 UInt32 iBuffer;
34968 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
34969 if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) {
34970 ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
34971 if (frameCountForThisBuffer > 0) {
34972 ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer);
34973 }
34974
34975 /*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);*/
34976 } else {
34977 /*
34978 This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
34979 not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just
34980 output silence here.
34981 */
34982 MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
34983 /*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);*/
34984 }
34985 }
34986 } else {
34987 /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */
34988 MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */
34989
34990 /*
34991 For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
34992 very strange has happened and we're not going to support it.
34993 */
34994 if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) {
34995 ma_uint8 tempBuffer[4096];
34996 UInt32 iBuffer;
34997
34998 for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) {
34999 ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat);
35000 ma_uint32 framesRemaining = frameCountPerBuffer;
35001
35002 while (framesRemaining > 0) {
35003 void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
35004 ma_uint32 iChannel;
35005 ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
35006 if (framesToRead > framesRemaining) {
35007 framesToRead = framesRemaining;
35008 }
35009
35010 ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead);
35011
35012 for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
35013 ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
35014 }
35015
35016 ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
35017
35018 framesRemaining -= framesToRead;
35019 }
35020 }
35021 }
35022 }
35023
35024 (void)pActionFlags;
35025 (void)pTimeStamp;
35026 (void)busNumber;
35027 (void)frameCount;
35028
35029 return noErr;
35030}
35031
35032static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList)
35033{
35034 ma_device* pDevice = (ma_device*)pUserData;
35035 AudioBufferList* pRenderedBufferList;
35036 ma_result result;
35037 ma_stream_layout layout;
35038 ma_uint32 iBuffer;
35039 OSStatus status;
35040
35041 MA_ASSERT(pDevice != NULL);
35042
35043 pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
35044 MA_ASSERT(pRenderedBufferList);
35045
35046 /* 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. */
35048 if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) {
35050 }
35051
35052 /*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);*/
35053
35054 /*
35055 There has been a situation reported where frame count passed into this function is greater than the capacity of
35056 our capture buffer. There doesn't seem to be a reliable way to determine what the maximum frame count will be,
35057 so we need to instead resort to dynamically reallocating our buffer to ensure it's large enough to capture the
35058 number of frames requested by this callback.
35059 */
35060 result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout);
35061 if (result != MA_SUCCESS) {
35062 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n");
35063 return noErr;
35064 }
35065
35066 pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
35067 MA_ASSERT(pRenderedBufferList);
35068
35069 /*
35070 When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes
35071 that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer
35072 being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a
35073 problem when a future call to this callback specifies a larger number of frames.
35074
35075 To work around this we need to explicitly set the size of each buffer to their respective size in bytes.
35076 */
35077 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
35078 pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels;
35079 /*printf("DEBUG: nDataByteSize = %d\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/
35080 }
35081
35082 status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList);
35083 if (status != noErr) {
35084 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "ERROR: AudioUnitRender() failed with %d.\n", (int)status);
35085 return status;
35086 }
35087
35088 if (layout == ma_stream_layout_interleaved) {
35089 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
35090 if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) {
35091 ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount);
35092 /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d.\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/
35093 } else {
35094 /*
35095 This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
35096 not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams.
35097 */
35098 ma_uint8 silentBuffer[4096];
35099 ma_uint32 framesRemaining;
35100
35101 MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer));
35102
35103 framesRemaining = frameCount;
35104 while (framesRemaining > 0) {
35105 ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
35106 if (framesToSend > framesRemaining) {
35107 framesToSend = framesRemaining;
35108 }
35109
35110 ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend);
35111
35112 framesRemaining -= framesToSend;
35113 }
35114
35115 /*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);*/
35116 }
35117 }
35118 } else {
35119 /* 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. */
35120 MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */
35121
35122 /*
35123 For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something
35124 very strange has happened and we're not going to support it.
35125 */
35126 if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) {
35127 ma_uint8 tempBuffer[4096];
35128 for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) {
35129 ma_uint32 framesRemaining = frameCount;
35130 while (framesRemaining > 0) {
35131 void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
35132 ma_uint32 iChannel;
35133 ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
35134 if (framesToSend > framesRemaining) {
35135 framesToSend = framesRemaining;
35136 }
35137
35138 for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
35139 ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
35140 }
35141
35142 ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);
35143 ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend);
35144
35145 framesRemaining -= framesToSend;
35146 }
35147 }
35148 }
35149 }
35150
35151 (void)pActionFlags;
35152 (void)pTimeStamp;
35153 (void)busNumber;
35154 (void)frameCount;
35155 (void)pUnusedBufferList;
35156
35157 return noErr;
35158}
35159
35160static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element)
35161{
35162 ma_device* pDevice = (ma_device*)pUserData;
35163 MA_ASSERT(pDevice != NULL);
35164
35165 /* Don't do anything if it looks like we're just reinitializing due to a device switch. */
35166 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
35167 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
35168 return;
35169 }
35170
35171 /*
35172 There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like
35173 AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit)
35174 can try waiting on the same lock. I'm going to try working around this by not calling any Core
35175 Audio APIs in the callback when the device has been stopped or uninitialized.
35176 */
35178 ma_device__on_notification_stopped(pDevice);
35179 } else {
35180 UInt32 isRunning;
35181 UInt32 isRunningSize = sizeof(isRunning);
35182 OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);
35183 if (status != noErr) {
35184 goto done; /* Don't really know what to do in this case... just ignore it, I suppose... */
35185 }
35186
35187 if (!isRunning) {
35188 /*
35189 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:
35190
35191 1) When the device is unplugged, this will be called _before_ the default device change notification.
35192 2) When the device is changed via the default device change notification, this will be called _after_ the switch.
35193
35194 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.
35195 */
35196 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) ||
35197 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) {
35198 /*
35199 It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device
35200 via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the
35201 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
35202 hasn't!).
35203 */
35204 if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
35205 ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
35206 goto done;
35207 }
35208
35209 /*
35210 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
35211 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
35212 likely be successful in switching to the new device.
35213
35214 TODO: Try to predict if Core Audio will switch devices. If not, the stopped callback needs to be posted.
35215 */
35216 goto done;
35217 }
35218
35219 /* Getting here means we need to stop the device. */
35220 ma_device__on_notification_stopped(pDevice);
35221 }
35222 }
35223
35224 (void)propertyID; /* Unused. */
35225
35226done:
35227 /* Always signal the stop event. It's possible for the "else" case to get hit which can happen during an interruption. */
35228 ma_event_signal(&pDevice->coreaudio.stopEvent);
35229}
35230
35231#if defined(MA_APPLE_DESKTOP)
35232static 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. */
35233static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0;
35234static ma_mutex g_DeviceTrackingMutex_CoreAudio;
35235static ma_device** g_ppTrackedDevices_CoreAudio = NULL;
35236static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0;
35237static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0;
35238
35239static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData)
35240{
35241 ma_device_type deviceType;
35242
35243 /* Not sure if I really need to check this, but it makes me feel better. */
35244 if (addressCount == 0) {
35245 return noErr;
35246 }
35247
35248 if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) {
35249 deviceType = ma_device_type_playback;
35250 } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) {
35251 deviceType = ma_device_type_capture;
35252 } else {
35253 return noErr; /* Should never hit this. */
35254 }
35255
35256 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
35257 {
35258 ma_uint32 iDevice;
35259 for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
35260 ma_result reinitResult;
35261 ma_device* pDevice;
35262
35263 pDevice = g_ppTrackedDevices_CoreAudio[iDevice];
35264 if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) {
35265 if (deviceType == ma_device_type_playback) {
35266 pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE;
35267 reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
35268 pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE;
35269 } else {
35270 pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE;
35271 reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
35272 pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE;
35273 }
35274
35275 if (reinitResult == MA_SUCCESS) {
35276 ma_device__post_init_setup(pDevice, deviceType);
35277
35278 /* Restart the device if required. If this fails we need to stop the device entirely. */
35280 OSStatus status;
35281 if (deviceType == ma_device_type_playback) {
35282 status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
35283 if (status != noErr) {
35284 if (pDevice->type == ma_device_type_duplex) {
35285 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
35286 }
35287 ma_device__set_state(pDevice, ma_device_state_stopped);
35288 }
35289 } else if (deviceType == ma_device_type_capture) {
35290 status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
35291 if (status != noErr) {
35292 if (pDevice->type == ma_device_type_duplex) {
35293 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
35294 }
35295 ma_device__set_state(pDevice, ma_device_state_stopped);
35296 }
35297 }
35298 }
35299
35300 ma_device__on_notification_rerouted(pDevice);
35301 }
35302 }
35303 }
35304 }
35305 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
35306
35307 /* Unused parameters. */
35308 (void)objectID;
35309 (void)pUserData;
35310
35311 return noErr;
35312}
35313
35314static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext)
35315{
35316 MA_ASSERT(pContext != NULL);
35317
35318 ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
35319 {
35320 /* Don't do anything if we've already initialized device tracking. */
35321 if (g_DeviceTrackingInitCounter_CoreAudio == 0) {
35322 AudioObjectPropertyAddress propAddress;
35323 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
35324 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
35325
35326 ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio);
35327
35328 propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
35329 ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
35330
35331 propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
35332 ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
35333
35334 }
35335 g_DeviceTrackingInitCounter_CoreAudio += 1;
35336 }
35337 ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
35338
35339 return MA_SUCCESS;
35340}
35341
35342static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext)
35343{
35344 MA_ASSERT(pContext != NULL);
35345
35346 ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
35347 {
35348 if (g_DeviceTrackingInitCounter_CoreAudio > 0)
35349 g_DeviceTrackingInitCounter_CoreAudio -= 1;
35350
35351 if (g_DeviceTrackingInitCounter_CoreAudio == 0) {
35352 AudioObjectPropertyAddress propAddress;
35353 propAddress.mScope = kAudioObjectPropertyScopeGlobal;
35354 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
35355
35356 propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
35357 ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
35358
35359 propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
35360 ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
35361
35362 /* At this point there should be no tracked devices. If not there's an error somewhere. */
35363 if (g_ppTrackedDevices_CoreAudio != NULL) {
35364 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active.");
35365 ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
35366 return MA_INVALID_OPERATION;
35367 }
35368
35369 ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio);
35370 }
35371 }
35372 ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
35373
35374 return MA_SUCCESS;
35375}
35376
35377static ma_result ma_device__track__coreaudio(ma_device* pDevice)
35378{
35379 MA_ASSERT(pDevice != NULL);
35380
35381 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
35382 {
35383 /* Allocate memory if required. */
35384 if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) {
35385 ma_uint32 newCap;
35386 ma_device** ppNewDevices;
35387
35388 newCap = g_TrackedDeviceCap_CoreAudio * 2;
35389 if (newCap == 0) {
35390 newCap = 1;
35391 }
35392
35393 ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks);
35394 if (ppNewDevices == NULL) {
35395 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
35396 return MA_OUT_OF_MEMORY;
35397 }
35398
35399 g_ppTrackedDevices_CoreAudio = ppNewDevices;
35400 g_TrackedDeviceCap_CoreAudio = newCap;
35401 }
35402
35403 g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice;
35404 g_TrackedDeviceCount_CoreAudio += 1;
35405 }
35406 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
35407
35408 return MA_SUCCESS;
35409}
35410
35411static ma_result ma_device__untrack__coreaudio(ma_device* pDevice)
35412{
35413 MA_ASSERT(pDevice != NULL);
35414
35415 ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
35416 {
35417 ma_uint32 iDevice;
35418 for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
35419 if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) {
35420 /* We've found the device. We now need to remove it from the list. */
35421 ma_uint32 jDevice;
35422 for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) {
35423 g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1];
35424 }
35425
35426 g_TrackedDeviceCount_CoreAudio -= 1;
35427
35428 /* If there's nothing else in the list we need to free memory. */
35429 if (g_TrackedDeviceCount_CoreAudio == 0) {
35430 ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks);
35431 g_ppTrackedDevices_CoreAudio = NULL;
35432 g_TrackedDeviceCap_CoreAudio = 0;
35433 }
35434
35435 break;
35436 }
35437 }
35438 }
35439 ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
35440
35441 return MA_SUCCESS;
35442}
35443#endif
35444
35445#if defined(MA_APPLE_MOBILE)
35446@interface ma_ios_notification_handler:NSObject {
35447 ma_device* m_pDevice;
35448}
35449@end
35450
35451@implementation ma_ios_notification_handler
35452-(id)init:(ma_device*)pDevice
35453{
35454 self = [super init];
35455 m_pDevice = pDevice;
35456
35457 /* For route changes. */
35458 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
35459
35460 /* For interruptions. */
35461 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
35462
35463 return self;
35464}
35465
35466-(void)dealloc
35467{
35468 [self remove_handler];
35469
35470 #if defined(__has_feature)
35471 #if !__has_feature(objc_arc)
35472 [super dealloc];
35473 #endif
35474 #endif
35475}
35476
35477-(void)remove_handler
35478{
35479 [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
35480 [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil];
35481}
35482
35483-(void)handle_interruption:(NSNotification*)pNotification
35484{
35485 NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue];
35486 switch (type)
35487 {
35488 case AVAudioSessionInterruptionTypeBegan:
35489 {
35490 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n");
35491
35492 /*
35493 Core Audio will have stopped the internal device automatically, but we need explicitly
35494 stop it at a higher level to ensure miniaudio-specific state is updated for consistency.
35495 */
35496 ma_device_stop(m_pDevice);
35497
35498 /*
35499 Fire the notification after the device has been stopped to ensure it's in the correct
35500 state when the notification handler is invoked.
35501 */
35502 ma_device__on_notification_interruption_began(m_pDevice);
35503 } break;
35504
35505 case AVAudioSessionInterruptionTypeEnded:
35506 {
35507 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n");
35508 ma_device__on_notification_interruption_ended(m_pDevice);
35509 } break;
35510 }
35511}
35512
35513-(void)handle_route_change:(NSNotification*)pNotification
35514{
35515 AVAudioSession* pSession = [AVAudioSession sharedInstance];
35516
35517 NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
35518 switch (reason)
35519 {
35520 case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
35521 {
35522 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n");
35523 } break;
35524
35525 case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
35526 {
35527 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n");
35528 } break;
35529
35530 case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
35531 {
35532 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n");
35533 } break;
35534
35535 case AVAudioSessionRouteChangeReasonWakeFromSleep:
35536 {
35537 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n");
35538 } break;
35539
35540 case AVAudioSessionRouteChangeReasonOverride:
35541 {
35542 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n");
35543 } break;
35544
35545 case AVAudioSessionRouteChangeReasonCategoryChange:
35546 {
35547 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n");
35548 } break;
35549
35550 case AVAudioSessionRouteChangeReasonUnknown:
35551 default:
35552 {
35553 ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n");
35554 } break;
35555 }
35556
35557 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);
35558
35559 /* Let the application know about the route change. */
35560 ma_device__on_notification_rerouted(m_pDevice);
35561}
35562@end
35563#endif
35564
35565static ma_result ma_device_uninit__coreaudio(ma_device* pDevice)
35566{
35567 MA_ASSERT(pDevice != NULL);
35569
35570#if defined(MA_APPLE_DESKTOP)
35571 /*
35572 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
35573 just gracefully ignore it.
35574 */
35575 ma_device__untrack__coreaudio(pDevice);
35576#endif
35577#if defined(MA_APPLE_MOBILE)
35578 if (pDevice->coreaudio.pNotificationHandler != NULL) {
35579 ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler;
35580 [pNotificationHandler remove_handler];
35581 }
35582#endif
35583
35584 if (pDevice->coreaudio.audioUnitCapture != NULL) {
35585 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
35586 }
35587 if (pDevice->coreaudio.audioUnitPlayback != NULL) {
35588 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
35589 }
35590
35591 if (pDevice->coreaudio.pAudioBufferList) {
35592 ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
35593 }
35594
35595 return MA_SUCCESS;
35596}
35597
35598typedef struct
35599{
35600 ma_bool32 allowNominalSampleRateChange;
35601
35602 /* Input. */
35603 ma_format formatIn;
35604 ma_uint32 channelsIn;
35605 ma_uint32 sampleRateIn;
35606 ma_channel channelMapIn[MA_MAX_CHANNELS];
35607 ma_uint32 periodSizeInFramesIn;
35608 ma_uint32 periodSizeInMillisecondsIn;
35609 ma_uint32 periodsIn;
35610 ma_share_mode shareMode;
35611 ma_performance_profile performanceProfile;
35612 ma_bool32 registerStopEvent;
35613
35614 /* Output. */
35615#if defined(MA_APPLE_DESKTOP)
35616 AudioObjectID deviceObjectID;
35617#endif
35618 AudioComponent component;
35619 AudioUnit audioUnit;
35620 AudioBufferList* pAudioBufferList; /* Only used for input devices. */
35621 ma_format formatOut;
35622 ma_uint32 channelsOut;
35623 ma_uint32 sampleRateOut;
35624 ma_channel channelMapOut[MA_MAX_CHANNELS];
35625 ma_uint32 periodSizeInFramesOut;
35626 ma_uint32 periodsOut;
35627 char deviceName[256];
35628} ma_device_init_internal_data__coreaudio;
35629
35630static 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. */
35631{
35632 ma_result result = MA_SUCCESS;
35633 OSStatus status;
35634 UInt32 enableIOFlag;
35635 AudioStreamBasicDescription bestFormat;
35636 ma_uint32 actualPeriodSizeInFrames;
35637 AURenderCallbackStruct callbackInfo;
35638#if defined(MA_APPLE_DESKTOP)
35639 AudioObjectID deviceObjectID;
35640#endif
35641
35642 /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */
35643 if (deviceType == ma_device_type_duplex) {
35644 return MA_INVALID_ARGS;
35645 }
35646
35647 MA_ASSERT(pContext != NULL);
35648 MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture);
35649
35650#if defined(MA_APPLE_DESKTOP)
35651 pData->deviceObjectID = 0;
35652#endif
35653 pData->component = NULL;
35654 pData->audioUnit = NULL;
35655 pData->pAudioBufferList = NULL;
35656
35657#if defined(MA_APPLE_DESKTOP)
35658 result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
35659 if (result != MA_SUCCESS) {
35660 return result;
35661 }
35662
35663 pData->deviceObjectID = deviceObjectID;
35664#endif
35665
35666 /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */
35667 pData->periodsOut = pData->periodsIn;
35668 if (pData->periodsOut == 0) {
35669 pData->periodsOut = MA_DEFAULT_PERIODS;
35670 }
35671 if (pData->periodsOut > 16) {
35672 pData->periodsOut = 16;
35673 }
35674
35675
35676 /* Audio unit. */
35677 status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit);
35678 if (status != noErr) {
35679 return ma_result_from_OSStatus(status);
35680 }
35681
35682
35683 /* 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. */
35684 enableIOFlag = 1;
35685 if (deviceType == ma_device_type_capture) {
35686 enableIOFlag = 0;
35687 }
35688
35689 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
35690 if (status != noErr) {
35691 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
35692 return ma_result_from_OSStatus(status);
35693 }
35694
35695 enableIOFlag = (enableIOFlag == 0) ? 1 : 0;
35696 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
35697 if (status != noErr) {
35698 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
35699 return ma_result_from_OSStatus(status);
35700 }
35701
35702
35703 /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */
35704#if defined(MA_APPLE_DESKTOP)
35705 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID));
35706 if (status != noErr) {
35707 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
35708 return ma_result_from_OSStatus(result);
35709 }
35710#else
35711 /*
35712 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
35713 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.
35714 */
35715 if (pDeviceID != NULL) {
35716 if (deviceType == ma_device_type_capture) {
35717 ma_bool32 found = MA_FALSE;
35718 NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
35719 for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
35720 if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
35721 [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil];
35722 found = MA_TRUE;
35723 break;
35724 }
35725 }
35726
35727 if (found == MA_FALSE) {
35728 return MA_DOES_NOT_EXIST;
35729 }
35730 }
35731 }
35732#endif
35733
35734 /*
35735 Format. This is the hardest part of initialization because there's a few variables to take into account.
35736 1) The format must be supported by the device.
35737 2) The format must be supported miniaudio.
35738 3) There's a priority that miniaudio prefers.
35739
35740 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
35741 most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same
35742 for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely.
35743
35744 On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to.
35745 */
35746 {
35747 AudioStreamBasicDescription origFormat;
35748 UInt32 origFormatSize = sizeof(origFormat);
35749 AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
35750 AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
35751
35752 if (deviceType == ma_device_type_playback) {
35753 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize);
35754 } else {
35755 status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize);
35756 }
35757 if (status != noErr) {
35758 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
35759 return ma_result_from_OSStatus(status);
35760 }
35761
35762 #if defined(MA_APPLE_DESKTOP)
35763 result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat);
35764 if (result != MA_SUCCESS) {
35765 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
35766 return result;
35767 }
35768
35769 /*
35770 Technical Note TN2091: Device input using the HAL Output Audio Unit
35771 https://developer.apple.com/library/archive/technotes/tn2091/_index.html
35772
35773 This documentation says the following:
35774
35775 The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY
35776 variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate
35777 conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with
35778 another AudioConverter.
35779
35780 The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We
35781 therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it
35782 safe and apply the same rule to output as well.
35783
35784 I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender()
35785 returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but
35786 this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format.
35787
35788 Something that does seem to work, however, has been setting the nominal sample rate on the device object. The problem with
35789 this, however, is that it actually changes the sample rate at the operating system level and not just the application. This
35790 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
35791 configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample
35792 rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run
35793 the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is
35794 changed by miniaudio.
35795 */
35796 if (pData->allowNominalSampleRateChange) {
35797 AudioValueRange sampleRateRange;
35798 AudioObjectPropertyAddress propAddress;
35799
35800 sampleRateRange.mMinimum = bestFormat.mSampleRate;
35801 sampleRateRange.mMaximum = bestFormat.mSampleRate;
35802
35803 propAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
35804 propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
35805 propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
35806
35807 status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange);
35808 if (status != noErr) {
35809 bestFormat.mSampleRate = origFormat.mSampleRate;
35810 }
35811 } else {
35812 bestFormat.mSampleRate = origFormat.mSampleRate;
35813 }
35814
35815 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
35816 if (status != noErr) {
35817 /* We failed to set the format, so fall back to the current format of the audio unit. */
35818 bestFormat = origFormat;
35819 }
35820 #else
35821 bestFormat = origFormat;
35822
35823 /*
35824 Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try
35825 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
35826 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
35827 can tell, it looks like the sample rate is shared between playback and capture for everything.
35828 */
35829 @autoreleasepool {
35830 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
35831 MA_ASSERT(pAudioSession != NULL);
35832
35833 [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil];
35834 bestFormat.mSampleRate = pAudioSession.sampleRate;
35835
35836 /*
35837 I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with
35838 AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead.
35839
35840 UPDATE 20/02/2025:
35841 When testing on the simulator with an iPhone 15 and iOS 17 I get an error when initializing the audio
35842 unit if set the input channels to pAudioSession.inputNumberOfChannels. What is happening is the channel
35843 count returned from AudioUnitGetProperty() above is set to 2, but pAudioSession is reporting a channel
35844 count of 1. When this happens, the call to AudioUnitSetProprty() below just down below will succeed, but
35845 AudioUnitInitialize() further down will fail. The only solution I have come up with is to not set the
35846 channel count to pAudioSession.inputNumberOfChannels.
35847 */
35848 if (deviceType == ma_device_type_playback) {
35849 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels;
35850 }
35851
35852 #if 0
35853 if (deviceType == ma_device_type_capture) {
35854 /*printf("DEBUG: bestFormat.mChannelsPerFrame = %d; pAudioSession.inputNumberOfChannels = %d\n", (int)bestFormat.mChannelsPerFrame, (int)pAudioSession.inputNumberOfChannels);*/
35855 bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels;
35856 }
35857 #endif
35858 }
35859
35860
35861 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
35862 if (status != noErr) {
35863 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
35864 return ma_result_from_OSStatus(status);
35865 }
35866 #endif
35867
35868 result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut);
35869 if (result != MA_SUCCESS) {
35870 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
35871 return result;
35872 }
35873
35874 if (pData->formatOut == ma_format_unknown) {
35875 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
35877 }
35878
35879 pData->channelsOut = bestFormat.mChannelsPerFrame;
35880 pData->sampleRateOut = (ma_uint32)bestFormat.mSampleRate;
35881 }
35882
35883 /* Clamp the channel count for safety. */
35884 if (pData->channelsOut > MA_MAX_CHANNELS) {
35885 pData->channelsOut = MA_MAX_CHANNELS;
35886 }
35887
35888 /*
35889 Internal channel map. This is weird in my testing. If I use the AudioObject to get the
35890 channel map, the channel descriptions are set to "Unknown" for some reason. To work around
35891 this it looks like retrieving it from the AudioUnit will work. However, and this is where
35892 it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore
35893 I'm going to fall back to a default assumption in these cases.
35894 */
35895#if defined(MA_APPLE_DESKTOP)
35896 result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut);
35897 if (result != MA_SUCCESS) {
35898 #if 0
35899 /* Try falling back to the channel map from the AudioObject. */
35900 result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut);
35901 if (result != MA_SUCCESS) {
35902 return result;
35903 }
35904 #else
35905 /* Fall back to default assumptions. */
35906 ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);
35907 #endif
35908 }
35909#else
35910 /* TODO: Figure out how to get the channel map using AVAudioSession. */
35911 ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);
35912#endif
35913
35914
35915 /* Buffer size. Not allowing this to be configurable on iOS. */
35916 if (pData->periodSizeInFramesIn == 0) {
35917 if (pData->periodSizeInMillisecondsIn == 0) {
35918 if (pData->performanceProfile == ma_performance_profile_low_latency) {
35919 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut);
35920 } else {
35921 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut);
35922 }
35923 } else {
35924 actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut);
35925 }
35926 } else {
35927 actualPeriodSizeInFrames = pData->periodSizeInFramesIn;
35928 }
35929
35930#if defined(MA_APPLE_DESKTOP)
35931 result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames);
35932 if (result != MA_SUCCESS) {
35933 return result;
35934 }
35935#else
35936 /*
35937 On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point
35938 number. I don't trust any potential truncation errors due to converting from float to integer
35939 so I'm going to explicitly set the actual period size to the next power of 2.
35940 */
35941 @autoreleasepool {
35942 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
35943 MA_ASSERT(pAudioSession != NULL);
35944
35945 [pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil];
35946 actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate));
35947 }
35948#endif
35949
35950
35951 /*
35952 During testing I discovered that the buffer size can be too big. You'll get an error like this:
35953
35954 kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512
35955
35956 Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that
35957 of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice.
35958 */
35959 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames));
35960 if (status != noErr) {
35961 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
35962 return ma_result_from_OSStatus(status);
35963 }
35964
35965 pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames;
35966
35967 /* We need a buffer list if this is an input device. We render into this in the input callback. */
35968 if (deviceType == ma_device_type_capture) {
35969 ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
35970 AudioBufferList* pBufferList;
35971
35972 pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks);
35973 if (pBufferList == NULL) {
35974 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
35975 return MA_OUT_OF_MEMORY;
35976 }
35977
35978 pData->pAudioBufferList = pBufferList;
35979 }
35980
35981 /* Callbacks. */
35982 callbackInfo.inputProcRefCon = pDevice_DoNotReference;
35983 if (deviceType == ma_device_type_playback) {
35984 callbackInfo.inputProc = ma_on_output__coreaudio;
35985 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));
35986 if (status != noErr) {
35987 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
35988 return ma_result_from_OSStatus(status);
35989 }
35990 } else {
35991 callbackInfo.inputProc = ma_on_input__coreaudio;
35992 status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));
35993 if (status != noErr) {
35994 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
35995 return ma_result_from_OSStatus(status);
35996 }
35997 }
35998
35999 /* We need to listen for stop events. */
36000 if (pData->registerStopEvent) {
36001 status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference);
36002 if (status != noErr) {
36003 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
36004 return ma_result_from_OSStatus(status);
36005 }
36006 }
36007
36008 /* Initialize the audio unit. */
36009 status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit);
36010 if (status != noErr) {
36011 ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks);
36012 pData->pAudioBufferList = NULL;
36013 ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
36014 return ma_result_from_OSStatus(status);
36015 }
36016
36017 /* Grab the name. */
36018#if defined(MA_APPLE_DESKTOP)
36019 ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName);
36020#else
36021 if (deviceType == ma_device_type_playback) {
36022 ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
36023 } else {
36024 ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME);
36025 }
36026#endif
36027
36028 return result;
36029}
36030
36031#if defined(MA_APPLE_DESKTOP)
36032static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit)
36033{
36034 ma_device_init_internal_data__coreaudio data;
36035 ma_result result;
36036
36037 /* This should only be called for playback or capture, not duplex. */
36038 if (deviceType == ma_device_type_duplex) {
36039 return MA_INVALID_ARGS;
36040 }
36041
36042 data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */
36043
36044 if (deviceType == ma_device_type_capture) {
36045 data.formatIn = pDevice->capture.format;
36046 data.channelsIn = pDevice->capture.channels;
36047 data.sampleRateIn = pDevice->sampleRate;
36048 MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
36049 data.shareMode = pDevice->capture.shareMode;
36050 data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile;
36051 data.registerStopEvent = MA_TRUE;
36052
36053 if (disposePreviousAudioUnit) {
36054 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
36055 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
36056 }
36057 if (pDevice->coreaudio.pAudioBufferList) {
36058 ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
36059 }
36060 } else if (deviceType == ma_device_type_playback) {
36061 data.formatIn = pDevice->playback.format;
36062 data.channelsIn = pDevice->playback.channels;
36063 data.sampleRateIn = pDevice->sampleRate;
36064 MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
36065 data.shareMode = pDevice->playback.shareMode;
36066 data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile;
36067 data.registerStopEvent = (pDevice->type != ma_device_type_duplex);
36068
36069 if (disposePreviousAudioUnit) {
36070 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
36071 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
36072 }
36073 }
36074 data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames;
36075 data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds;
36076 data.periodsIn = pDevice->coreaudio.originalPeriods;
36077
36078 /* Need at least 3 periods for duplex. */
36079 if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) {
36080 data.periodsIn = 3;
36081 }
36082
36083 result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice);
36084 if (result != MA_SUCCESS) {
36085 return result;
36086 }
36087
36088 if (deviceType == ma_device_type_capture) {
36089 #if defined(MA_APPLE_DESKTOP)
36090 pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
36091 ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio);
36092 #endif
36093 pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
36094 pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
36095 pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut;
36096
36097 pDevice->capture.internalFormat = data.formatOut;
36098 pDevice->capture.internalChannels = data.channelsOut;
36099 pDevice->capture.internalSampleRate = data.sampleRateOut;
36100 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
36101 pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
36102 pDevice->capture.internalPeriods = data.periodsOut;
36103 } else if (deviceType == ma_device_type_playback) {
36104 #if defined(MA_APPLE_DESKTOP)
36105 pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
36106 ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio);
36107 #endif
36108 pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
36109
36110 pDevice->playback.internalFormat = data.formatOut;
36111 pDevice->playback.internalChannels = data.channelsOut;
36112 pDevice->playback.internalSampleRate = data.sampleRateOut;
36113 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
36114 pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
36115 pDevice->playback.internalPeriods = data.periodsOut;
36116 }
36117
36118 return MA_SUCCESS;
36119}
36120#endif /* MA_APPLE_DESKTOP */
36121
36122static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
36123{
36124 ma_result result;
36125
36126 MA_ASSERT(pDevice != NULL);
36127 MA_ASSERT(pConfig != NULL);
36128
36129 if (pConfig->deviceType == ma_device_type_loopback) {
36131 }
36132
36133 /* No exclusive mode with the Core Audio backend for now. */
36134 if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) ||
36135 ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) {
36137 }
36138
36139 /* Capture needs to be initialized first. */
36140 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
36141 ma_device_init_internal_data__coreaudio data;
36142 data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange;
36143 data.formatIn = pDescriptorCapture->format;
36144 data.channelsIn = pDescriptorCapture->channels;
36145 data.sampleRateIn = pDescriptorCapture->sampleRate;
36146 MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
36147 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
36148 data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds;
36149 data.periodsIn = pDescriptorCapture->periodCount;
36150 data.shareMode = pDescriptorCapture->shareMode;
36151 data.performanceProfile = pConfig->performanceProfile;
36152 data.registerStopEvent = MA_TRUE;
36153
36154 /* Need at least 3 periods for duplex. */
36155 if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) {
36156 data.periodsIn = 3;
36157 }
36158
36159 result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice);
36160 if (result != MA_SUCCESS) {
36161 return result;
36162 }
36163
36164 pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL);
36165 #if defined(MA_APPLE_DESKTOP)
36166 pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
36167 #endif
36168 pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
36169 pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
36170 pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut;
36171 pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
36172 pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;
36173 pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount;
36174 pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile;
36175
36176 pDescriptorCapture->format = data.formatOut;
36177 pDescriptorCapture->channels = data.channelsOut;
36178 pDescriptorCapture->sampleRate = data.sampleRateOut;
36179 MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
36180 pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut;
36181 pDescriptorCapture->periodCount = data.periodsOut;
36182
36183 #if defined(MA_APPLE_DESKTOP)
36184 ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio);
36185
36186 /*
36187 If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly
36188 switch the device in the background.
36189 */
36190 if (pConfig->capture.pDeviceID == NULL) {
36191 ma_device__track__coreaudio(pDevice);
36192 }
36193 #endif
36194 }
36195
36196 /* Playback. */
36197 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
36198 ma_device_init_internal_data__coreaudio data;
36199 data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange;
36200 data.formatIn = pDescriptorPlayback->format;
36201 data.channelsIn = pDescriptorPlayback->channels;
36202 data.sampleRateIn = pDescriptorPlayback->sampleRate;
36203 MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
36204 data.shareMode = pDescriptorPlayback->shareMode;
36205 data.performanceProfile = pConfig->performanceProfile;
36206
36207 /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */
36208 if (pConfig->deviceType == ma_device_type_duplex) {
36209 data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
36210 data.periodsIn = pDescriptorCapture->periodCount;
36211 data.registerStopEvent = MA_FALSE;
36212 } else {
36213 data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames;
36214 data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
36215 data.periodsIn = pDescriptorPlayback->periodCount;
36216 data.registerStopEvent = MA_TRUE;
36217 }
36218
36219 result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice);
36220 if (result != MA_SUCCESS) {
36221 if (pConfig->deviceType == ma_device_type_duplex) {
36222 ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
36223 if (pDevice->coreaudio.pAudioBufferList) {
36224 ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
36225 }
36226 }
36227 return result;
36228 }
36229
36230 pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL);
36231 #if defined(MA_APPLE_DESKTOP)
36232 pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
36233 #endif
36234 pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
36235 pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
36236 pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
36237 pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount;
36238 pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile;
36239
36240 pDescriptorPlayback->format = data.formatOut;
36241 pDescriptorPlayback->channels = data.channelsOut;
36242 pDescriptorPlayback->sampleRate = data.sampleRateOut;
36243 MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
36244 pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut;
36245 pDescriptorPlayback->periodCount = data.periodsOut;
36246
36247 #if defined(MA_APPLE_DESKTOP)
36248 ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio);
36249
36250 /*
36251 If we are using the default device we'll need to listen for changes to the system's default device so we can seamlessly
36252 switch the device in the background.
36253 */
36254 if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) {
36255 ma_device__track__coreaudio(pDevice);
36256 }
36257 #endif
36258 }
36259
36260
36261
36262 /*
36263 When stopping the device, a callback is called on another thread. We need to wait for this callback
36264 before returning from ma_device_stop(). This event is used for this.
36265 */
36266 ma_event_init(&pDevice->coreaudio.stopEvent);
36267
36268 /*
36269 We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done
36270 differently on non-Desktop Apple platforms.
36271 */
36272#if defined(MA_APPLE_MOBILE)
36273 pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice];
36274#endif
36275
36276 return MA_SUCCESS;
36277}
36278
36279
36280static ma_result ma_device_start__coreaudio(ma_device* pDevice)
36281{
36282 MA_ASSERT(pDevice != NULL);
36283
36284 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
36285 OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
36286 if (status != noErr) {
36287 return ma_result_from_OSStatus(status);
36288 }
36289 }
36290
36291 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
36292 OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
36293 if (status != noErr) {
36294 if (pDevice->type == ma_device_type_duplex) {
36295 ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
36296 }
36297 return ma_result_from_OSStatus(status);
36298 }
36299 }
36300
36301 return MA_SUCCESS;
36302}
36303
36304static ma_result ma_device_stop__coreaudio(ma_device* pDevice)
36305{
36306 MA_ASSERT(pDevice != NULL);
36307
36308 /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */
36309
36310 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
36311 OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
36312 if (status != noErr) {
36313 return ma_result_from_OSStatus(status);
36314 }
36315 }
36316
36317 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
36318 OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
36319 if (status != noErr) {
36320 return ma_result_from_OSStatus(status);
36321 }
36322 }
36323
36324 /* We need to wait for the callback to finish before returning. */
36325 ma_event_wait(&pDevice->coreaudio.stopEvent);
36326 return MA_SUCCESS;
36327}
36328
36329
36330static ma_result ma_context_uninit__coreaudio(ma_context* pContext)
36331{
36332 MA_ASSERT(pContext != NULL);
36333 MA_ASSERT(pContext->backend == ma_backend_coreaudio);
36334
36335#if defined(MA_APPLE_MOBILE)
36336 if (!pContext->coreaudio.noAudioSessionDeactivate) {
36337 if (![[AVAudioSession sharedInstance] setActive:false error:nil]) {
36338 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session.");
36340 }
36341 }
36342#endif
36343
36344#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
36345 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit);
36346 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);
36347 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
36348#endif
36349
36350#if !defined(MA_APPLE_MOBILE)
36351 ma_context__uninit_device_tracking__coreaudio(pContext);
36352#endif
36353
36354 (void)pContext;
36355 return MA_SUCCESS;
36356}
36357
36358#if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0)
36359static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category)
36360{
36361 /* The "default" and "none" categories are treated different and should not be used as an input into this function. */
36362 MA_ASSERT(category != ma_ios_session_category_default);
36363 MA_ASSERT(category != ma_ios_session_category_none);
36364
36365 switch (category) {
36366 case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient;
36367 case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient;
36368 case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback;
36369 case ma_ios_session_category_record: return AVAudioSessionCategoryRecord;
36370 case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord;
36371 case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute;
36372 case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient;
36373 case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient;
36374 default: return AVAudioSessionCategoryAmbient;
36375 }
36376}
36377#endif
36378
36379static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
36380{
36381#if !defined(MA_APPLE_MOBILE)
36382 ma_result result;
36383#endif
36384
36385 MA_ASSERT(pConfig != NULL);
36386 MA_ASSERT(pContext != NULL);
36387
36388#if defined(MA_APPLE_MOBILE)
36389 @autoreleasepool {
36390 AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
36391 AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions;
36392
36393 MA_ASSERT(pAudioSession != NULL);
36394
36396 /*
36397 I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails
36398 we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category.
36399 */
36400 #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH)
36401 options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
36402 #endif
36403
36404 if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) {
36405 /* Using PlayAndRecord */
36406 } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) {
36407 /* Using Playback */
36408 } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) {
36409 /* Using Record */
36410 } else {
36411 /* Leave as default? */
36412 }
36413 } else {
36415 #if defined(__IPHONE_12_0)
36416 if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) {
36417 return MA_INVALID_OPERATION; /* Failed to set session category. */
36418 }
36419 #else
36420 /* Ignore the session category on version 11 and older, but post a warning. */
36421 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer.");
36422 #endif
36423 }
36424 }
36425
36426 if (!pConfig->coreaudio.noAudioSessionActivate) {
36427 if (![pAudioSession setActive:true error:nil]) {
36428 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session.");
36430 }
36431 }
36432 }
36433#endif
36434
36435#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
36436 pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation");
36437 if (pContext->coreaudio.hCoreFoundation == NULL) {
36438 return MA_API_NOT_FOUND;
36439 }
36440
36441 pContext->coreaudio.CFStringGetCString = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFStringGetCString");
36442 pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease");
36443
36444
36445 pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreAudio.framework/CoreAudio");
36446 if (pContext->coreaudio.hCoreAudio == NULL) {
36447 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
36448 return MA_API_NOT_FOUND;
36449 }
36450
36451 pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData");
36452 pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize");
36453 pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData");
36454 pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener");
36455 pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener");
36456
36457 /*
36458 It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still
36459 defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback.
36460 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
36461 AudioToolbox.
36462 */
36463 pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioUnit.framework/AudioUnit");
36464 if (pContext->coreaudio.hAudioUnit == NULL) {
36465 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);
36466 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
36467 return MA_API_NOT_FOUND;
36468 }
36469
36470 if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) {
36471 /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */
36472 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit);
36473 pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox");
36474 if (pContext->coreaudio.hAudioUnit == NULL) {
36475 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);
36476 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
36477 return MA_API_NOT_FOUND;
36478 }
36479 }
36480
36481 pContext->coreaudio.AudioComponentFindNext = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext");
36482 pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose");
36483 pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew");
36484 pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart");
36485 pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop");
36486 pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener");
36487 pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo");
36488 pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty");
36489 pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty");
36490 pContext->coreaudio.AudioUnitInitialize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitInitialize");
36491 pContext->coreaudio.AudioUnitRender = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitRender");
36492#else
36493 pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString;
36494 pContext->coreaudio.CFRelease = (ma_proc)CFRelease;
36495
36496 #if defined(MA_APPLE_DESKTOP)
36497 pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData;
36498 pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize;
36499 pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData;
36500 pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener;
36501 pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener;
36502 #endif
36503
36504 pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext;
36505 pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose;
36506 pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew;
36507 pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart;
36508 pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop;
36509 pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener;
36510 pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo;
36511 pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty;
36512 pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty;
36513 pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize;
36514 pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender;
36515#endif
36516
36517 /* Audio component. */
36518 {
36519 AudioComponentDescription desc;
36520 desc.componentType = kAudioUnitType_Output;
36521 #if defined(MA_APPLE_DESKTOP)
36522 desc.componentSubType = kAudioUnitSubType_HALOutput;
36523 #else
36524 desc.componentSubType = kAudioUnitSubType_RemoteIO;
36525 #endif
36526 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
36527 desc.componentFlags = 0;
36528 desc.componentFlagsMask = 0;
36529
36530 pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
36531 if (pContext->coreaudio.component == NULL) {
36532 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
36533 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit);
36534 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);
36535 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
36536 #endif
36538 }
36539 }
36540
36541#if !defined(MA_APPLE_MOBILE)
36542 result = ma_context__init_device_tracking__coreaudio(pContext);
36543 if (result != MA_SUCCESS) {
36544 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
36545 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit);
36546 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);
36547 ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
36548 #endif
36549 return result;
36550 }
36551#endif
36552
36553 pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate;
36554
36555 pCallbacks->onContextInit = ma_context_init__coreaudio;
36556 pCallbacks->onContextUninit = ma_context_uninit__coreaudio;
36557 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio;
36558 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio;
36559 pCallbacks->onDeviceInit = ma_device_init__coreaudio;
36560 pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio;
36561 pCallbacks->onDeviceStart = ma_device_start__coreaudio;
36562 pCallbacks->onDeviceStop = ma_device_stop__coreaudio;
36563 pCallbacks->onDeviceRead = NULL;
36564 pCallbacks->onDeviceWrite = NULL;
36565 pCallbacks->onDeviceDataLoop = NULL;
36566
36567 return MA_SUCCESS;
36568}
36569#endif /* MA_HAS_COREAUDIO */
36570
36571
36572
36573/******************************************************************************
36574
36575sndio Backend
36576
36577******************************************************************************/
36578#ifdef MA_HAS_SNDIO
36579#include <fcntl.h>
36580
36581/*
36582Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due
36583to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device
36584just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's
36585demand for it or if I can get it tested and debugged more thoroughly.
36586*/
36587#if 0
36588#if defined(__NetBSD__) || defined(__OpenBSD__)
36589#include <sys/audioio.h>
36590#endif
36591#if defined(__FreeBSD__) || defined(__DragonFly__)
36592#include <sys/soundcard.h>
36593#endif
36594#endif
36595
36596#define MA_SIO_DEVANY "default"
36597#define MA_SIO_PLAY 1
36598#define MA_SIO_REC 2
36599#define MA_SIO_NENC 8
36600#define MA_SIO_NCHAN 8
36601#define MA_SIO_NRATE 16
36602#define MA_SIO_NCONF 4
36603
36604struct ma_sio_hdl; /* <-- Opaque */
36605
36606struct ma_sio_par
36607{
36608 unsigned int bits;
36609 unsigned int bps;
36610 unsigned int sig;
36611 unsigned int le;
36612 unsigned int msb;
36613 unsigned int rchan;
36614 unsigned int pchan;
36615 unsigned int rate;
36616 unsigned int bufsz;
36617 unsigned int xrun;
36618 unsigned int round;
36619 unsigned int appbufsz;
36620 int __pad[3];
36621 unsigned int __magic;
36622};
36623
36624struct ma_sio_enc
36625{
36626 unsigned int bits;
36627 unsigned int bps;
36628 unsigned int sig;
36629 unsigned int le;
36630 unsigned int msb;
36631};
36632
36633struct ma_sio_conf
36634{
36635 unsigned int enc;
36636 unsigned int rchan;
36637 unsigned int pchan;
36638 unsigned int rate;
36639};
36640
36641struct ma_sio_cap
36642{
36643 struct ma_sio_enc enc[MA_SIO_NENC];
36644 unsigned int rchan[MA_SIO_NCHAN];
36645 unsigned int pchan[MA_SIO_NCHAN];
36646 unsigned int rate[MA_SIO_NRATE];
36647 int __pad[7];
36648 unsigned int nconf;
36649 struct ma_sio_conf confs[MA_SIO_NCONF];
36650};
36651
36652typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int);
36653typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*);
36654typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
36655typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
36656typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*);
36657typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t);
36658typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t);
36659typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*);
36660typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*);
36661typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*);
36662
36663static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */
36664{
36665 ma_uint32 i;
36666 for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) {
36667 if (g_maStandardSampleRatePriorities[i] == sampleRate) {
36668 return i;
36669 }
36670 }
36671
36672 return (ma_uint32)-1;
36673}
36674
36675static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb)
36676{
36677 /* We only support native-endian right now. */
36678 if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) {
36679 return ma_format_unknown;
36680 }
36681
36682 if (bits == 8 && bps == 1 && sig == 0) {
36683 return ma_format_u8;
36684 }
36685 if (bits == 16 && bps == 2 && sig == 1) {
36686 return ma_format_s16;
36687 }
36688 if (bits == 24 && bps == 3 && sig == 1) {
36689 return ma_format_s24;
36690 }
36691 if (bits == 24 && bps == 4 && sig == 1 && msb == 0) {
36692 /*return ma_format_s24_32;*/
36693 }
36694 if (bits == 32 && bps == 4 && sig == 1) {
36695 return ma_format_s32;
36696 }
36697
36698 return ma_format_unknown;
36699}
36700
36701static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps)
36702{
36703 ma_format bestFormat;
36704 unsigned int iConfig;
36705
36706 MA_ASSERT(caps != NULL);
36707
36708 bestFormat = ma_format_unknown;
36709 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
36710 unsigned int iEncoding;
36711 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
36712 unsigned int bits;
36713 unsigned int bps;
36714 unsigned int sig;
36715 unsigned int le;
36716 unsigned int msb;
36717 ma_format format;
36718
36719 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
36720 continue;
36721 }
36722
36723 bits = caps->enc[iEncoding].bits;
36724 bps = caps->enc[iEncoding].bps;
36725 sig = caps->enc[iEncoding].sig;
36726 le = caps->enc[iEncoding].le;
36727 msb = caps->enc[iEncoding].msb;
36728 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
36729 if (format == ma_format_unknown) {
36730 continue; /* Format not supported. */
36731 }
36732
36733 if (bestFormat == ma_format_unknown) {
36734 bestFormat = format;
36735 } else {
36736 if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */
36737 bestFormat = format;
36738 }
36739 }
36740 }
36741 }
36742
36743 return bestFormat;
36744}
36745
36746static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat)
36747{
36748 ma_uint32 maxChannels;
36749 unsigned int iConfig;
36750
36751 MA_ASSERT(caps != NULL);
36752 MA_ASSERT(requiredFormat != ma_format_unknown);
36753
36754 /* Just pick whatever configuration has the most channels. */
36755 maxChannels = 0;
36756 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
36757 /* The encoding should be of requiredFormat. */
36758 unsigned int iEncoding;
36759 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
36760 unsigned int iChannel;
36761 unsigned int bits;
36762 unsigned int bps;
36763 unsigned int sig;
36764 unsigned int le;
36765 unsigned int msb;
36766 ma_format format;
36767
36768 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
36769 continue;
36770 }
36771
36772 bits = caps->enc[iEncoding].bits;
36773 bps = caps->enc[iEncoding].bps;
36774 sig = caps->enc[iEncoding].sig;
36775 le = caps->enc[iEncoding].le;
36776 msb = caps->enc[iEncoding].msb;
36777 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
36778 if (format != requiredFormat) {
36779 continue;
36780 }
36781
36782 /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
36783 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
36784 unsigned int chan = 0;
36785 unsigned int channels;
36786
36787 if (deviceType == ma_device_type_playback) {
36788 chan = caps->confs[iConfig].pchan;
36789 } else {
36790 chan = caps->confs[iConfig].rchan;
36791 }
36792
36793 if ((chan & (1UL << iChannel)) == 0) {
36794 continue;
36795 }
36796
36797 if (deviceType == ma_device_type_playback) {
36798 channels = caps->pchan[iChannel];
36799 } else {
36800 channels = caps->rchan[iChannel];
36801 }
36802
36803 if (maxChannels < channels) {
36804 maxChannels = channels;
36805 }
36806 }
36807 }
36808 }
36809
36810 return maxChannels;
36811}
36812
36813static 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)
36814{
36815 ma_uint32 firstSampleRate;
36816 ma_uint32 bestSampleRate;
36817 unsigned int iConfig;
36818
36819 MA_ASSERT(caps != NULL);
36820 MA_ASSERT(requiredFormat != ma_format_unknown);
36821 MA_ASSERT(requiredChannels > 0);
36822 MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS);
36823
36824 firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */
36825 bestSampleRate = 0;
36826
36827 for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
36828 /* The encoding should be of requiredFormat. */
36829 unsigned int iEncoding;
36830 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
36831 unsigned int iChannel;
36832 unsigned int bits;
36833 unsigned int bps;
36834 unsigned int sig;
36835 unsigned int le;
36836 unsigned int msb;
36837 ma_format format;
36838
36839 if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
36840 continue;
36841 }
36842
36843 bits = caps->enc[iEncoding].bits;
36844 bps = caps->enc[iEncoding].bps;
36845 sig = caps->enc[iEncoding].sig;
36846 le = caps->enc[iEncoding].le;
36847 msb = caps->enc[iEncoding].msb;
36848 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
36849 if (format != requiredFormat) {
36850 continue;
36851 }
36852
36853 /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
36854 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
36855 unsigned int chan = 0;
36856 unsigned int channels;
36857 unsigned int iRate;
36858
36859 if (deviceType == ma_device_type_playback) {
36860 chan = caps->confs[iConfig].pchan;
36861 } else {
36862 chan = caps->confs[iConfig].rchan;
36863 }
36864
36865 if ((chan & (1UL << iChannel)) == 0) {
36866 continue;
36867 }
36868
36869 if (deviceType == ma_device_type_playback) {
36870 channels = caps->pchan[iChannel];
36871 } else {
36872 channels = caps->rchan[iChannel];
36873 }
36874
36875 if (channels != requiredChannels) {
36876 continue;
36877 }
36878
36879 /* Getting here means we have found a compatible encoding/channel pair. */
36880 for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
36881 ma_uint32 rate = (ma_uint32)caps->rate[iRate];
36882 ma_uint32 ratePriority;
36883
36884 if (firstSampleRate == 0) {
36885 firstSampleRate = rate;
36886 }
36887
36888 /* Disregard this rate if it's not a standard one. */
36889 ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate);
36890 if (ratePriority == (ma_uint32)-1) {
36891 continue;
36892 }
36893
36894 if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) { /* Lower = better. */
36895 bestSampleRate = rate;
36896 }
36897 }
36898 }
36899 }
36900 }
36901
36902 /* If a standard sample rate was not found just fall back to the first one that was iterated. */
36903 if (bestSampleRate == 0) {
36904 bestSampleRate = firstSampleRate;
36905 }
36906
36907 return bestSampleRate;
36908}
36909
36910
36911static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
36912{
36913 ma_bool32 isTerminating = MA_FALSE;
36914 struct ma_sio_hdl* handle;
36915
36916 MA_ASSERT(pContext != NULL);
36917 MA_ASSERT(callback != NULL);
36918
36919 /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */
36920
36921 /* Playback. */
36922 if (!isTerminating) {
36923 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0);
36924 if (handle != NULL) {
36925 /* Supports playback. */
36926 ma_device_info deviceInfo;
36927 MA_ZERO_OBJECT(&deviceInfo);
36928 ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY);
36929 ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
36930
36931 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
36932
36933 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
36934 }
36935 }
36936
36937 /* Capture. */
36938 if (!isTerminating) {
36939 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0);
36940 if (handle != NULL) {
36941 /* Supports capture. */
36942 ma_device_info deviceInfo;
36943 MA_ZERO_OBJECT(&deviceInfo);
36944 ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default");
36945 ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME);
36946
36947 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
36948
36949 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
36950 }
36951 }
36952
36953 return MA_SUCCESS;
36954}
36955
36956static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
36957{
36958 char devid[256];
36959 struct ma_sio_hdl* handle;
36960 struct ma_sio_cap caps;
36961 unsigned int iConfig;
36962
36963 MA_ASSERT(pContext != NULL);
36964
36965 /* We need to open the device before we can get information about it. */
36966 if (pDeviceID == NULL) {
36967 ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY);
36968 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME);
36969 } else {
36970 ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio);
36971 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid);
36972 }
36973
36974 handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0);
36975 if (handle == NULL) {
36976 return MA_NO_DEVICE;
36977 }
36978
36979 if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) {
36980 return MA_ERROR;
36981 }
36982
36983 pDeviceInfo->nativeDataFormatCount = 0;
36984
36985 for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) {
36986 /*
36987 The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give
36988 preference to some formats over others.
36989 */
36990 unsigned int iEncoding;
36991 unsigned int iChannel;
36992 unsigned int iRate;
36993
36994 for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
36995 unsigned int bits;
36996 unsigned int bps;
36997 unsigned int sig;
36998 unsigned int le;
36999 unsigned int msb;
37000 ma_format format;
37001
37002 if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) {
37003 continue;
37004 }
37005
37006 bits = caps.enc[iEncoding].bits;
37007 bps = caps.enc[iEncoding].bps;
37008 sig = caps.enc[iEncoding].sig;
37009 le = caps.enc[iEncoding].le;
37010 msb = caps.enc[iEncoding].msb;
37011 format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
37012 if (format == ma_format_unknown) {
37013 continue; /* Format not supported. */
37014 }
37015
37016
37017 /* Channels. */
37018 for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
37019 unsigned int chan = 0;
37020 unsigned int channels;
37021
37022 if (deviceType == ma_device_type_playback) {
37023 chan = caps.confs[iConfig].pchan;
37024 } else {
37025 chan = caps.confs[iConfig].rchan;
37026 }
37027
37028 if ((chan & (1UL << iChannel)) == 0) {
37029 continue;
37030 }
37031
37032 if (deviceType == ma_device_type_playback) {
37033 channels = caps.pchan[iChannel];
37034 } else {
37035 channels = caps.rchan[iChannel];
37036 }
37037
37038
37039 /* Sample Rates. */
37040 for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
37041 if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) {
37042 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0);
37043 }
37044 }
37045 }
37046 }
37047 }
37048
37049 ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
37050 return MA_SUCCESS;
37051}
37052
37053static ma_result ma_device_uninit__sndio(ma_device* pDevice)
37054{
37055 MA_ASSERT(pDevice != NULL);
37056
37057 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
37058 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
37059 }
37060
37061 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
37062 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
37063 }
37064
37065 return MA_SUCCESS;
37066}
37067
37068static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
37069{
37070 const char* pDeviceName;
37071 ma_ptr handle;
37072 int openFlags = 0;
37073 struct ma_sio_cap caps;
37074 struct ma_sio_par par;
37075 const ma_device_id* pDeviceID;
37076 ma_format format;
37077 ma_uint32 channels;
37078 ma_uint32 sampleRate;
37079 ma_format internalFormat;
37080 ma_uint32 internalChannels;
37081 ma_uint32 internalSampleRate;
37082 ma_uint32 internalPeriodSizeInFrames;
37083 ma_uint32 internalPeriods;
37084
37085 MA_ASSERT(pConfig != NULL);
37086 MA_ASSERT(deviceType != ma_device_type_duplex);
37087 MA_ASSERT(pDevice != NULL);
37088
37089 if (deviceType == ma_device_type_capture) {
37090 openFlags = MA_SIO_REC;
37091 } else {
37092 openFlags = MA_SIO_PLAY;
37093 }
37094
37095 pDeviceID = pDescriptor->pDeviceID;
37096 format = pDescriptor->format;
37097 channels = pDescriptor->channels;
37098 sampleRate = pDescriptor->sampleRate;
37099
37100 pDeviceName = MA_SIO_DEVANY;
37101 if (pDeviceID != NULL) {
37102 pDeviceName = pDeviceID->sndio;
37103 }
37104
37105 handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0);
37106 if (handle == NULL) {
37107 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device.");
37109 }
37110
37111 /* We need to retrieve the device caps to determine the most appropriate format to use. */
37112 if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) {
37113 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
37114 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps.");
37115 return MA_ERROR;
37116 }
37117
37118 /*
37119 Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real
37120 way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this
37121 to the requested channels, regardless of whether or not the default channel count is requested.
37122
37123 For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the
37124 value returned by ma_find_best_channels_from_sio_cap__sndio().
37125 */
37126 if (deviceType == ma_device_type_capture) {
37127 if (format == ma_format_unknown) {
37128 format = ma_find_best_format_from_sio_cap__sndio(&caps);
37129 }
37130
37131 if (channels == 0) {
37132 if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
37133 channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
37134 } else {
37135 channels = MA_DEFAULT_CHANNELS;
37136 }
37137 }
37138 } else {
37139 if (format == ma_format_unknown) {
37140 format = ma_find_best_format_from_sio_cap__sndio(&caps);
37141 }
37142
37143 if (channels == 0) {
37144 if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
37145 channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
37146 } else {
37147 channels = MA_DEFAULT_CHANNELS;
37148 }
37149 }
37150 }
37151
37152 if (sampleRate == 0) {
37153 sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels);
37154 }
37155
37156
37157 ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par);
37158 par.msb = 0;
37159 par.le = ma_is_little_endian();
37160
37161 switch (format) {
37162 case ma_format_u8:
37163 {
37164 par.bits = 8;
37165 par.bps = 1;
37166 par.sig = 0;
37167 } break;
37168
37169 case ma_format_s24:
37170 {
37171 par.bits = 24;
37172 par.bps = 3;
37173 par.sig = 1;
37174 } break;
37175
37176 case ma_format_s32:
37177 {
37178 par.bits = 32;
37179 par.bps = 4;
37180 par.sig = 1;
37181 } break;
37182
37183 case ma_format_s16:
37184 case ma_format_f32:
37185 case ma_format_unknown:
37186 default:
37187 {
37188 par.bits = 16;
37189 par.bps = 2;
37190 par.sig = 1;
37191 } break;
37192 }
37193
37194 if (deviceType == ma_device_type_capture) {
37195 par.rchan = channels;
37196 } else {
37197 par.pchan = channels;
37198 }
37199
37200 par.rate = sampleRate;
37201
37202 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile);
37203
37204 par.round = internalPeriodSizeInFrames;
37205 par.appbufsz = par.round * pDescriptor->periodCount;
37206
37207 if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) {
37208 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
37209 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.");
37210 return MA_ERROR;
37211 }
37212
37213 if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) {
37214 ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
37215 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size.");
37216 return MA_ERROR;
37217 }
37218
37219 internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb);
37220 internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan;
37221 internalSampleRate = par.rate;
37222 internalPeriods = par.appbufsz / par.round;
37223 internalPeriodSizeInFrames = par.round;
37224
37225 if (deviceType == ma_device_type_capture) {
37226 pDevice->sndio.handleCapture = handle;
37227 } else {
37228 pDevice->sndio.handlePlayback = handle;
37229 }
37230
37231 pDescriptor->format = internalFormat;
37232 pDescriptor->channels = internalChannels;
37233 pDescriptor->sampleRate = internalSampleRate;
37234 ma_channel_map_init_standard(ma_standard_channel_map_sndio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels);
37235 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
37236 pDescriptor->periodCount = internalPeriods;
37237
37238 return MA_SUCCESS;
37239}
37240
37241static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
37242{
37243 MA_ASSERT(pDevice != NULL);
37244
37245 MA_ZERO_OBJECT(&pDevice->sndio);
37246
37247 if (pConfig->deviceType == ma_device_type_loopback) {
37249 }
37250
37251 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
37252 ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
37253 if (result != MA_SUCCESS) {
37254 return result;
37255 }
37256 }
37257
37258 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
37259 ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
37260 if (result != MA_SUCCESS) {
37261 return result;
37262 }
37263 }
37264
37265 return MA_SUCCESS;
37266}
37267
37268static ma_result ma_device_start__sndio(ma_device* pDevice)
37269{
37270 MA_ASSERT(pDevice != NULL);
37271
37272 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
37273 ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
37274 }
37275
37276 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
37277 ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */
37278 }
37279
37280 return MA_SUCCESS;
37281}
37282
37283static ma_result ma_device_stop__sndio(ma_device* pDevice)
37284{
37285 MA_ASSERT(pDevice != NULL);
37286
37287 /*
37288 From the documentation:
37289
37290 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
37291 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
37292 buffer is drained. In no case are samples in the play buffer discarded.
37293
37294 Therefore, sio_stop() performs all of the necessary draining for us.
37295 */
37296
37297 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
37298 ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
37299 }
37300
37301 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
37302 ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
37303 }
37304
37305 return MA_SUCCESS;
37306}
37307
37308static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
37309{
37310 int result;
37311
37312 if (pFramesWritten != NULL) {
37313 *pFramesWritten = 0;
37314 }
37315
37316 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));
37317 if (result == 0) {
37318 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device.");
37319 return MA_IO_ERROR;
37320 }
37321
37322 if (pFramesWritten != NULL) {
37323 *pFramesWritten = frameCount;
37324 }
37325
37326 return MA_SUCCESS;
37327}
37328
37329static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
37330{
37331 int result;
37332
37333 if (pFramesRead != NULL) {
37334 *pFramesRead = 0;
37335 }
37336
37337 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));
37338 if (result == 0) {
37339 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.");
37340 return MA_IO_ERROR;
37341 }
37342
37343 if (pFramesRead != NULL) {
37344 *pFramesRead = frameCount;
37345 }
37346
37347 return MA_SUCCESS;
37348}
37349
37350static ma_result ma_context_uninit__sndio(ma_context* pContext)
37351{
37352 MA_ASSERT(pContext != NULL);
37353 MA_ASSERT(pContext->backend == ma_backend_sndio);
37354
37355 (void)pContext;
37356 return MA_SUCCESS;
37357}
37358
37359static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
37360{
37361#ifndef MA_NO_RUNTIME_LINKING
37362 const char* libsndioNames[] = {
37363 "libsndio.so"
37364 };
37365 size_t i;
37366
37367 for (i = 0; i < ma_countof(libsndioNames); ++i) {
37368 pContext->sndio.sndioSO = ma_dlopen(ma_context_get_log(pContext), libsndioNames[i]);
37369 if (pContext->sndio.sndioSO != NULL) {
37370 break;
37371 }
37372 }
37373
37374 if (pContext->sndio.sndioSO == NULL) {
37375 return MA_NO_BACKEND;
37376 }
37377
37378 pContext->sndio.sio_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_open");
37379 pContext->sndio.sio_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_close");
37380 pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_setpar");
37381 pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getpar");
37382 pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getcap");
37383 pContext->sndio.sio_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_write");
37384 pContext->sndio.sio_read = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_read");
37385 pContext->sndio.sio_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_start");
37386 pContext->sndio.sio_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_stop");
37387 pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_initpar");
37388#else
37389 pContext->sndio.sio_open = sio_open;
37390 pContext->sndio.sio_close = sio_close;
37391 pContext->sndio.sio_setpar = sio_setpar;
37392 pContext->sndio.sio_getpar = sio_getpar;
37393 pContext->sndio.sio_getcap = sio_getcap;
37394 pContext->sndio.sio_write = sio_write;
37395 pContext->sndio.sio_read = sio_read;
37396 pContext->sndio.sio_start = sio_start;
37397 pContext->sndio.sio_stop = sio_stop;
37398 pContext->sndio.sio_initpar = sio_initpar;
37399#endif
37400
37401 pCallbacks->onContextInit = ma_context_init__sndio;
37402 pCallbacks->onContextUninit = ma_context_uninit__sndio;
37403 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio;
37404 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio;
37405 pCallbacks->onDeviceInit = ma_device_init__sndio;
37406 pCallbacks->onDeviceUninit = ma_device_uninit__sndio;
37407 pCallbacks->onDeviceStart = ma_device_start__sndio;
37408 pCallbacks->onDeviceStop = ma_device_stop__sndio;
37409 pCallbacks->onDeviceRead = ma_device_read__sndio;
37410 pCallbacks->onDeviceWrite = ma_device_write__sndio;
37411 pCallbacks->onDeviceDataLoop = NULL;
37412
37413 (void)pConfig;
37414 return MA_SUCCESS;
37415}
37416#endif /* MA_HAS_SNDIO */
37417
37418
37419
37420/******************************************************************************
37421
37422audio(4) Backend
37423
37424******************************************************************************/
37425#ifdef MA_HAS_AUDIO4
37426#include <fcntl.h>
37427#include <poll.h>
37428#include <errno.h>
37429#include <sys/stat.h>
37430#include <sys/types.h>
37431#include <sys/ioctl.h>
37432#include <sys/audioio.h>
37433
37434#ifdef __NetBSD__
37435#include <sys/param.h>
37436#endif
37437
37438#if defined(__OpenBSD__)
37439 #include <sys/param.h>
37440 #if defined(OpenBSD) && OpenBSD >= 201709
37441 #define MA_AUDIO4_USE_NEW_API
37442 #endif
37443#endif
37444
37445static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex)
37446{
37447 size_t baseLen;
37448
37449 MA_ASSERT(id != NULL);
37450 MA_ASSERT(idSize > 0);
37451 MA_ASSERT(deviceIndex >= 0);
37452
37453 baseLen = strlen(base);
37454 MA_ASSERT(idSize > baseLen);
37455
37456 ma_strcpy_s(id, idSize, base);
37457 ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10);
37458}
37459
37460static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut)
37461{
37462 size_t idLen;
37463 size_t baseLen;
37464 const char* deviceIndexStr;
37465
37466 MA_ASSERT(id != NULL);
37467 MA_ASSERT(base != NULL);
37468 MA_ASSERT(pIndexOut != NULL);
37469
37470 idLen = strlen(id);
37471 baseLen = strlen(base);
37472 if (idLen <= baseLen) {
37473 return MA_ERROR; /* Doesn't look like the id starts with the base. */
37474 }
37475
37476 if (strncmp(id, base, baseLen) != 0) {
37477 return MA_ERROR; /* ID does not begin with base. */
37478 }
37479
37480 deviceIndexStr = id + baseLen;
37481 if (deviceIndexStr[0] == '\0') {
37482 return MA_ERROR; /* No index specified in the ID. */
37483 }
37484
37485 if (pIndexOut) {
37486 *pIndexOut = atoi(deviceIndexStr);
37487 }
37488
37489 return MA_SUCCESS;
37490}
37491
37492
37493#if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
37494static ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision)
37495{
37496 if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) {
37497 return ma_format_u8;
37498 } else {
37499 if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) {
37500 if (precision == 16) {
37501 return ma_format_s16;
37502 } else if (precision == 24) {
37503 return ma_format_s24;
37504 } else if (precision == 32) {
37505 return ma_format_s32;
37506 }
37507 } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) {
37508 if (precision == 16) {
37509 return ma_format_s16;
37510 } else if (precision == 24) {
37511 return ma_format_s24;
37512 } else if (precision == 32) {
37513 return ma_format_s32;
37514 }
37515 }
37516 }
37517
37518 return ma_format_unknown; /* Encoding not supported. */
37519}
37520
37521static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision)
37522{
37523 MA_ASSERT(pEncoding != NULL);
37524 MA_ASSERT(pPrecision != NULL);
37525
37526 switch (format)
37527 {
37528 case ma_format_u8:
37529 {
37530 *pEncoding = AUDIO_ENCODING_ULINEAR;
37531 *pPrecision = 8;
37532 } break;
37533
37534 case ma_format_s24:
37535 {
37536 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
37537 *pPrecision = 24;
37538 } break;
37539
37540 case ma_format_s32:
37541 {
37542 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
37543 *pPrecision = 32;
37544 } break;
37545
37546 case ma_format_s16:
37547 case ma_format_f32:
37548 case ma_format_unknown:
37549 default:
37550 {
37551 *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
37552 *pPrecision = 16;
37553 } break;
37554 }
37555}
37556
37557static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo)
37558{
37559 return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision);
37560}
37561
37562static ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat)
37563{
37564 audio_encoding_t encoding;
37565 ma_uint32 iFormat;
37566 int counter = 0;
37567
37568 /* First check to see if the preferred format is supported. */
37569 if (preferredFormat != ma_format_unknown) {
37570 counter = 0;
37571 for (;;) {
37572 MA_ZERO_OBJECT(&encoding);
37573 encoding.index = counter;
37574 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
37575 break;
37576 }
37577
37578 if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) {
37579 return preferredFormat; /* Found the preferred format. */
37580 }
37581
37582 /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */
37583 counter += 1;
37584 }
37585 }
37586
37587 /* Getting here means our preferred format is not supported, so fall back to our standard priorities. */
37588 for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
37589 ma_format format = g_maFormatPriorities[iFormat];
37590
37591 counter = 0;
37592 for (;;) {
37593 MA_ZERO_OBJECT(&encoding);
37594 encoding.index = counter;
37595 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
37596 break;
37597 }
37598
37599 if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) {
37600 return format; /* Found a workable format. */
37601 }
37602
37603 /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */
37604 counter += 1;
37605 }
37606 }
37607
37608 /* Getting here means not appropriate format was found. */
37609 return ma_format_unknown;
37610}
37611#else
37612static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par)
37613{
37614 if (par->bits == 8 && par->bps == 1 && par->sig == 0) {
37615 return ma_format_u8;
37616 }
37617 if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) {
37618 return ma_format_s16;
37619 }
37620 if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) {
37621 return ma_format_s24;
37622 }
37623 if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) {
37624 return ma_format_f32;
37625 }
37626
37627 /* Format not supported. */
37628 return ma_format_unknown;
37629}
37630#endif
37631
37632static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo)
37633{
37634 audio_device_t fdDevice;
37635
37636 MA_ASSERT(pContext != NULL);
37637 MA_ASSERT(fd >= 0);
37638 MA_ASSERT(pDeviceInfo != NULL);
37639
37640 (void)pContext;
37641 (void)deviceType;
37642
37643 if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) {
37644 return MA_ERROR; /* Failed to retrieve device info. */
37645 }
37646
37647 /* Name. */
37648 ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name);
37649
37650 #if !defined(MA_AUDIO4_USE_NEW_API)
37651 {
37652 audio_info_t fdInfo;
37653 int counter = 0;
37654 ma_uint32 channels;
37655 ma_uint32 sampleRate;
37656
37657#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000)
37658 if (ioctl(fd, AUDIO_GETFORMAT, &fdInfo) < 0) {
37659 return MA_ERROR;
37660 }
37661#else
37662 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
37663 return MA_ERROR;
37664 }
37665#endif
37666
37667 if (deviceType == ma_device_type_playback) {
37668 channels = fdInfo.play.channels;
37669 sampleRate = fdInfo.play.sample_rate;
37670 } else {
37671 channels = fdInfo.record.channels;
37672 sampleRate = fdInfo.record.sample_rate;
37673 }
37674
37675 /* Supported formats. We get this by looking at the encodings. */
37676 pDeviceInfo->nativeDataFormatCount = 0;
37677 for (;;) {
37678 audio_encoding_t encoding;
37679 ma_format format;
37680
37681 MA_ZERO_OBJECT(&encoding);
37682 encoding.index = counter;
37683 if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
37684 break;
37685 }
37686
37687 format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision);
37688 if (format != ma_format_unknown) {
37689 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0);
37690 }
37691
37692 counter += 1;
37693 }
37694 }
37695 #else
37696 {
37697 struct audio_swpar fdPar;
37698 ma_format format;
37699 ma_uint32 channels;
37700 ma_uint32 sampleRate;
37701
37702 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
37703 return MA_ERROR;
37704 }
37705
37706 format = ma_format_from_swpar__audio4(&fdPar);
37707 if (format == ma_format_unknown) {
37709 }
37710
37711 if (deviceType == ma_device_type_playback) {
37712 channels = fdPar.pchan;
37713 } else {
37714 channels = fdPar.rchan;
37715 }
37716
37717 sampleRate = fdPar.rate;
37718
37719 pDeviceInfo->nativeDataFormatCount = 0;
37720 ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0);
37721 }
37722 #endif
37723
37724 return MA_SUCCESS;
37725}
37726
37727static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
37728{
37729 const int maxDevices = 64;
37730 char devpath[256];
37731 int iDevice;
37732
37733 MA_ASSERT(pContext != NULL);
37734 MA_ASSERT(callback != NULL);
37735
37736 /*
37737 Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN"
37738 version here since we can open it even when another process has control of the "/dev/audioN" device.
37739 */
37740 for (iDevice = 0; iDevice < maxDevices; ++iDevice) {
37741 struct stat st;
37742 int fd;
37743 ma_bool32 isTerminating = MA_FALSE;
37744
37745 ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl");
37746 ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10);
37747
37748 if (stat(devpath, &st) < 0) {
37749 break;
37750 }
37751
37752 /* The device exists, but we need to check if it's usable as playback and/or capture. */
37753
37754 /* Playback. */
37755 if (!isTerminating) {
37756 fd = open(devpath, O_RDONLY, 0);
37757 if (fd >= 0) {
37758 /* Supports playback. */
37759 ma_device_info deviceInfo;
37760 MA_ZERO_OBJECT(&deviceInfo);
37761 ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
37762 if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) {
37763 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
37764 }
37765
37766 close(fd);
37767 }
37768 }
37769
37770 /* Capture. */
37771 if (!isTerminating) {
37772 fd = open(devpath, O_WRONLY, 0);
37773 if (fd >= 0) {
37774 /* Supports capture. */
37775 ma_device_info deviceInfo;
37776 MA_ZERO_OBJECT(&deviceInfo);
37777 ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
37778 if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) {
37779 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
37780 }
37781
37782 close(fd);
37783 }
37784 }
37785
37786 if (isTerminating) {
37787 break;
37788 }
37789 }
37790
37791 return MA_SUCCESS;
37792}
37793
37794static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
37795{
37796 int fd = -1;
37797 int deviceIndex = -1;
37798 char ctlid[256];
37799 ma_result result;
37800
37801 MA_ASSERT(pContext != NULL);
37802
37803 /*
37804 We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number
37805 from the device ID which will be in "/dev/audioN" format.
37806 */
37807 if (pDeviceID == NULL) {
37808 /* Default device. */
37809 ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl");
37810 } else {
37811 /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */
37812 result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex);
37813 if (result != MA_SUCCESS) {
37814 return result;
37815 }
37816
37817 ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex);
37818 }
37819
37820 fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0);
37821 if (fd == -1) {
37822 return MA_NO_DEVICE;
37823 }
37824
37825 if (deviceIndex == -1) {
37826 ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio");
37827 } else {
37828 ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex);
37829 }
37830
37831 result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo);
37832
37833 close(fd);
37834 return result;
37835}
37836
37837static ma_result ma_device_uninit__audio4(ma_device* pDevice)
37838{
37839 MA_ASSERT(pDevice != NULL);
37840
37841 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
37842 close(pDevice->audio4.fdCapture);
37843 }
37844
37845 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
37846 close(pDevice->audio4.fdPlayback);
37847 }
37848
37849 return MA_SUCCESS;
37850}
37851
37852static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
37853{
37854 const char* pDefaultDeviceNames[] = {
37855 "/dev/audio",
37856 "/dev/audio0"
37857 };
37858 const char* pDefaultDeviceCtlNames[] = {
37859 "/dev/audioctl",
37860 "/dev/audioctl0"
37861 };
37862 int fd;
37863 int fdFlags = 0;
37864 size_t iDefaultDevice = (size_t)-1;
37865 ma_format internalFormat;
37866 ma_uint32 internalChannels;
37867 ma_uint32 internalSampleRate;
37868 ma_uint32 internalPeriodSizeInFrames;
37869 ma_uint32 internalPeriods;
37870
37871 MA_ASSERT(pConfig != NULL);
37872 MA_ASSERT(deviceType != ma_device_type_duplex);
37873 MA_ASSERT(pDevice != NULL);
37874
37875 /* The first thing to do is open the file. */
37876 if (deviceType == ma_device_type_capture) {
37877 fdFlags = O_RDONLY;
37878 } else {
37879 fdFlags = O_WRONLY;
37880 }
37881 /*fdFlags |= O_NONBLOCK;*/
37882
37883 /* Find the index of the default device as a start. We'll use this index later. Set it to (size_t)-1 otherwise. */
37884 if (pDescriptor->pDeviceID == NULL) {
37885 /* Default device. */
37886 for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); ++iDefaultDevice) {
37887 fd = open(pDefaultDeviceNames[iDefaultDevice], fdFlags, 0);
37888 if (fd != -1) {
37889 break;
37890 }
37891 }
37892 } else {
37893 /* Specific device. */
37894 fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0);
37895
37896 for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); iDefaultDevice += 1) {
37897 if (ma_strcmp(pDefaultDeviceNames[iDefaultDevice], pDescriptor->pDeviceID->audio4) == 0) {
37898 break;
37899 }
37900 }
37901
37902 if (iDefaultDevice == ma_countof(pDefaultDeviceNames)) {
37903 iDefaultDevice = (size_t)-1;
37904 }
37905 }
37906
37907 if (fd == -1) {
37908 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device.");
37909 return ma_result_from_errno(errno);
37910 }
37911
37912 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
37913 {
37914 audio_info_t fdInfo;
37915 int fdInfoResult = -1;
37916
37917 /*
37918 The documentation is a little bit unclear to me as to how it handles formats. It says the
37919 following:
37920
37921 Regardless of formats supported by underlying driver, the audio driver accepts the
37922 following formats.
37923
37924 By then the next sentence says this:
37925
37926 `encoding` and `precision` are one of the values obtained by AUDIO_GETENC.
37927
37928 It sounds like a direct contradiction to me. I'm going to play this safe any only use the
37929 best sample format returned by AUDIO_GETENC. If the requested format is supported we'll
37930 use that, but otherwise we'll just use our standard format priorities to pick an
37931 appropriate one.
37932 */
37933 AUDIO_INITINFO(&fdInfo);
37934
37935 /*
37936 Get the default format from the audioctl file if we're asking for a default device. If we
37937 retrieve it from /dev/audio it'll default to mono 8000Hz.
37938 */
37939 if (iDefaultDevice != (size_t)-1) {
37940 /* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */
37941 int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0);
37942 if (fdctl != -1) {
37943#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000)
37944 fdInfoResult = ioctl(fdctl, AUDIO_GETFORMAT, &fdInfo);
37945#else
37946 fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo);
37947#endif
37948 close(fdctl);
37949 }
37950 }
37951
37952 if (fdInfoResult == -1) {
37953 /* We still don't have the default device info so just retrieve it from the main audio device. */
37954 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
37955 close(fd);
37956 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.");
37957 return ma_result_from_errno(errno);
37958 }
37959 }
37960
37961 /* We get the driver to do as much of the data conversion as possible. */
37962 if (deviceType == ma_device_type_capture) {
37963 fdInfo.mode = AUMODE_RECORD;
37964 ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision);
37965
37966 if (pDescriptor->channels != 0) {
37967 fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */
37968 }
37969
37970 if (pDescriptor->sampleRate != 0) {
37971 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`.) */
37972 }
37973 } else {
37974 fdInfo.mode = AUMODE_PLAY;
37975 ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision);
37976
37977 if (pDescriptor->channels != 0) {
37978 fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */
37979 }
37980
37981 if (pDescriptor->sampleRate != 0) {
37982 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`.) */
37983 }
37984 }
37985
37986 if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
37987 close(fd);
37988 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed.");
37989 return ma_result_from_errno(errno);
37990 }
37991
37992 if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
37993 close(fd);
37994 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.");
37995 return ma_result_from_errno(errno);
37996 }
37997
37998 if (deviceType == ma_device_type_capture) {
37999 internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record);
38000 internalChannels = fdInfo.record.channels;
38001 internalSampleRate = fdInfo.record.sample_rate;
38002 } else {
38003 internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play);
38004 internalChannels = fdInfo.play.channels;
38005 internalSampleRate = fdInfo.play.sample_rate;
38006 }
38007
38008 if (internalFormat == ma_format_unknown) {
38009 close(fd);
38010 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.");
38012 }
38013
38014 /* Buffer. */
38015 {
38016 ma_uint32 internalPeriodSizeInBytes;
38017
38018 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile);
38019
38020 internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
38021 if (internalPeriodSizeInBytes < 16) {
38022 internalPeriodSizeInBytes = 16;
38023 }
38024
38025 internalPeriods = pDescriptor->periodCount;
38026 if (internalPeriods < 2) {
38027 internalPeriods = 2;
38028 }
38029
38030 /* What miniaudio calls a period, audio4 calls a block. */
38031 AUDIO_INITINFO(&fdInfo);
38032 fdInfo.hiwat = internalPeriods;
38033 fdInfo.lowat = internalPeriods-1;
38034 fdInfo.blocksize = internalPeriodSizeInBytes;
38035 if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
38036 close(fd);
38037 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed.");
38038 return ma_result_from_errno(errno);
38039 }
38040
38041 internalPeriods = fdInfo.hiwat;
38042 internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels);
38043 }
38044 }
38045 #else
38046 {
38047 struct audio_swpar fdPar;
38048
38049 /* 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. */
38050 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
38051 close(fd);
38052 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters.");
38053 return ma_result_from_errno(errno);
38054 }
38055
38056 internalFormat = ma_format_from_swpar__audio4(&fdPar);
38057 internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
38058 internalSampleRate = fdPar.rate;
38059
38060 if (internalFormat == ma_format_unknown) {
38061 close(fd);
38062 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.");
38064 }
38065
38066 /* Buffer. */
38067 {
38068 ma_uint32 internalPeriodSizeInBytes;
38069
38070 internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile);
38071
38072 /* What miniaudio calls a period, audio4 calls a block. */
38073 internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
38074 if (internalPeriodSizeInBytes < 16) {
38075 internalPeriodSizeInBytes = 16;
38076 }
38077
38078 fdPar.nblks = pDescriptor->periodCount;
38079 fdPar.round = internalPeriodSizeInBytes;
38080
38081 if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) {
38082 close(fd);
38083 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters.");
38084 return ma_result_from_errno(errno);
38085 }
38086
38087 if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
38088 close(fd);
38089 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters.");
38090 return ma_result_from_errno(errno);
38091 }
38092 }
38093
38094 internalFormat = ma_format_from_swpar__audio4(&fdPar);
38095 internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
38096 internalSampleRate = fdPar.rate;
38097 internalPeriods = fdPar.nblks;
38098 internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels);
38099 }
38100 #endif
38101
38102 if (internalFormat == ma_format_unknown) {
38103 close(fd);
38104 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.");
38106 }
38107
38108 if (deviceType == ma_device_type_capture) {
38109 pDevice->audio4.fdCapture = fd;
38110 } else {
38111 pDevice->audio4.fdPlayback = fd;
38112 }
38113
38114 pDescriptor->format = internalFormat;
38115 pDescriptor->channels = internalChannels;
38116 pDescriptor->sampleRate = internalSampleRate;
38117 ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels);
38118 pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
38119 pDescriptor->periodCount = internalPeriods;
38120
38121 return MA_SUCCESS;
38122}
38123
38124static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
38125{
38126 MA_ASSERT(pDevice != NULL);
38127
38128 MA_ZERO_OBJECT(&pDevice->audio4);
38129
38130 if (pConfig->deviceType == ma_device_type_loopback) {
38132 }
38133
38134 pDevice->audio4.fdCapture = -1;
38135 pDevice->audio4.fdPlayback = -1;
38136
38137 /*
38138 The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD
38139 introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as
38140 I'm aware.
38141 */
38142#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000
38143 /* NetBSD 8.0+ */
38144 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
38145 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
38147 }
38148#else
38149 /* All other flavors. */
38150#endif
38151
38152 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
38153 ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
38154 if (result != MA_SUCCESS) {
38155 return result;
38156 }
38157 }
38158
38159 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
38160 ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
38161 if (result != MA_SUCCESS) {
38162 if (pConfig->deviceType == ma_device_type_duplex) {
38163 close(pDevice->audio4.fdCapture);
38164 }
38165 return result;
38166 }
38167 }
38168
38169 return MA_SUCCESS;
38170}
38171
38172static ma_result ma_device_start__audio4(ma_device* pDevice)
38173{
38174 MA_ASSERT(pDevice != NULL);
38175
38176 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
38177 if (pDevice->audio4.fdCapture == -1) {
38178 return MA_INVALID_ARGS;
38179 }
38180 }
38181
38182 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
38183 if (pDevice->audio4.fdPlayback == -1) {
38184 return MA_INVALID_ARGS;
38185 }
38186 }
38187
38188 return MA_SUCCESS;
38189}
38190
38191static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd)
38192{
38193 if (fd == -1) {
38194 return MA_INVALID_ARGS;
38195 }
38196
38197#if !defined(MA_AUDIO4_USE_NEW_API)
38198 if (ioctl(fd, AUDIO_FLUSH, 0) < 0) {
38199 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed.");
38200 return ma_result_from_errno(errno);
38201 }
38202#else
38203 if (ioctl(fd, AUDIO_STOP, 0) < 0) {
38204 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed.");
38205 return ma_result_from_errno(errno);
38206 }
38207#endif
38208
38209 return MA_SUCCESS;
38210}
38211
38212static ma_result ma_device_stop__audio4(ma_device* pDevice)
38213{
38214 MA_ASSERT(pDevice != NULL);
38215
38216 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
38217 ma_result result;
38218
38219 result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture);
38220 if (result != MA_SUCCESS) {
38221 return result;
38222 }
38223 }
38224
38225 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
38226 ma_result result;
38227
38228 /* 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. */
38229 #if !defined(MA_AUDIO4_USE_NEW_API)
38230 ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0);
38231 #endif
38232
38233 /* Here is where the device is stopped immediately. */
38234 result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback);
38235 if (result != MA_SUCCESS) {
38236 return result;
38237 }
38238 }
38239
38240 return MA_SUCCESS;
38241}
38242
38243static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
38244{
38245 int result;
38246
38247 if (pFramesWritten != NULL) {
38248 *pFramesWritten = 0;
38249 }
38250
38251 result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
38252 if (result < 0) {
38253 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device.");
38254 return ma_result_from_errno(errno);
38255 }
38256
38257 if (pFramesWritten != NULL) {
38258 *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
38259 }
38260
38261 return MA_SUCCESS;
38262}
38263
38264static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
38265{
38266 int result;
38267
38268 if (pFramesRead != NULL) {
38269 *pFramesRead = 0;
38270 }
38271
38272 result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
38273 if (result < 0) {
38274 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device.");
38275 return ma_result_from_errno(errno);
38276 }
38277
38278 if (pFramesRead != NULL) {
38279 *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
38280 }
38281
38282 return MA_SUCCESS;
38283}
38284
38285static ma_result ma_context_uninit__audio4(ma_context* pContext)
38286{
38287 MA_ASSERT(pContext != NULL);
38288 MA_ASSERT(pContext->backend == ma_backend_audio4);
38289
38290 (void)pContext;
38291 return MA_SUCCESS;
38292}
38293
38294static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
38295{
38296 MA_ASSERT(pContext != NULL);
38297
38298 (void)pConfig;
38299
38300 pCallbacks->onContextInit = ma_context_init__audio4;
38301 pCallbacks->onContextUninit = ma_context_uninit__audio4;
38302 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4;
38303 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4;
38304 pCallbacks->onDeviceInit = ma_device_init__audio4;
38305 pCallbacks->onDeviceUninit = ma_device_uninit__audio4;
38306 pCallbacks->onDeviceStart = ma_device_start__audio4;
38307 pCallbacks->onDeviceStop = ma_device_stop__audio4;
38308 pCallbacks->onDeviceRead = ma_device_read__audio4;
38309 pCallbacks->onDeviceWrite = ma_device_write__audio4;
38310 pCallbacks->onDeviceDataLoop = NULL;
38311
38312 return MA_SUCCESS;
38313}
38314#endif /* MA_HAS_AUDIO4 */
38315
38316
38317/******************************************************************************
38318
38319OSS Backend
38320
38321******************************************************************************/
38322#ifdef MA_HAS_OSS
38323#include <sys/ioctl.h>
38324#include <unistd.h>
38325#include <fcntl.h>
38326#include <sys/soundcard.h>
38327
38328#ifndef SNDCTL_DSP_HALT
38329#define SNDCTL_DSP_HALT SNDCTL_DSP_RESET
38330#endif
38331
38332#define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp"
38333
38334static int ma_open_temp_device__oss(void)
38335{
38336 /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */
38337 int fd = open("/dev/mixer", O_RDONLY, 0);
38338 if (fd >= 0) {
38339 return fd;
38340 }
38341
38342 return -1;
38343}
38344
38345static 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)
38346{
38347 const char* deviceName;
38348 int flags;
38349
38350 MA_ASSERT(pContext != NULL);
38351 MA_ASSERT(pfd != NULL);
38352 (void)pContext;
38353
38354 *pfd = -1;
38355
38356 /* This function should only be called for playback or capture, not duplex. */
38357 if (deviceType == ma_device_type_duplex) {
38358 return MA_INVALID_ARGS;
38359 }
38360
38361 deviceName = MA_OSS_DEFAULT_DEVICE_NAME;
38362 if (pDeviceID != NULL) {
38363 deviceName = pDeviceID->oss;
38364 }
38365
38366 flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY;
38367 if (shareMode == ma_share_mode_exclusive) {
38368 flags |= O_EXCL;
38369 }
38370
38371 *pfd = open(deviceName, flags, 0);
38372 if (*pfd == -1) {
38373 return ma_result_from_errno(errno);
38374 }
38375
38376 return MA_SUCCESS;
38377}
38378
38379static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
38380{
38381 int fd;
38382 oss_sysinfo si;
38383 int result;
38384
38385 MA_ASSERT(pContext != NULL);
38386 MA_ASSERT(callback != NULL);
38387
38388 fd = ma_open_temp_device__oss();
38389 if (fd == -1) {
38390 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.");
38391 return MA_NO_BACKEND;
38392 }
38393
38394 result = ioctl(fd, SNDCTL_SYSINFO, &si);
38395 if (result != -1) {
38396 int iAudioDevice;
38397 for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
38398 oss_audioinfo ai;
38399 ai.dev = iAudioDevice;
38400 result = ioctl(fd, SNDCTL_AUDIOINFO, &ai);
38401 if (result != -1) {
38402 if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */
38403 ma_device_info deviceInfo;
38404 ma_bool32 isTerminating = MA_FALSE;
38405
38406 MA_ZERO_OBJECT(&deviceInfo);
38407
38408 /* ID */
38409 ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1);
38410
38411 /*
38412 The human readable device name should be in the "ai.handle" variable, but it can
38413 sometimes be empty in which case we just fall back to "ai.name" which is less user
38414 friendly, but usually has a value.
38415 */
38416 if (ai.handle[0] != '\0') {
38417 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1);
38418 } else {
38419 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1);
38420 }
38421
38422 /* The device can be both playback and capture. */
38423 if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) {
38424 isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
38425 }
38426 if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) {
38427 isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
38428 }
38429
38430 if (isTerminating) {
38431 break;
38432 }
38433 }
38434 }
38435 }
38436 } else {
38437 close(fd);
38438 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.");
38439 return MA_NO_BACKEND;
38440 }
38441
38442 close(fd);
38443 return MA_SUCCESS;
38444}
38445
38446static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo)
38447{
38448 unsigned int minChannels;
38449 unsigned int maxChannels;
38450 unsigned int iRate;
38451
38452 MA_ASSERT(pContext != NULL);
38453 MA_ASSERT(pAudioInfo != NULL);
38454 MA_ASSERT(pDeviceInfo != NULL);
38455
38456 /* If we support all channels we just report 0. */
38457 minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
38458 maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
38459
38460 /*
38461 OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness,
38462 which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which
38463 case we'll need to use min_rate and max_rate and report only standard rates.
38464 */
38465 if (pAudioInfo->nrates > 0) {
38466 for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) {
38467 unsigned int rate = pAudioInfo->rates[iRate];
38468
38469 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
38470 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. */
38471 } else {
38472 unsigned int iChannel;
38473 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
38474 ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0);
38475 }
38476 }
38477 }
38478 } else {
38479 for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) {
38480 ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate];
38481
38482 if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) {
38483 if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
38484 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. */
38485 } else {
38486 unsigned int iChannel;
38487 for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
38488 ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0);
38489 }
38490 }
38491 }
38492 }
38493 }
38494}
38495
38496static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
38497{
38498 ma_bool32 foundDevice;
38499 int fdTemp;
38500 oss_sysinfo si;
38501 int result;
38502
38503 MA_ASSERT(pContext != NULL);
38504
38505 /* Handle the default device a little differently. */
38506 if (pDeviceID == NULL) {
38507 if (deviceType == ma_device_type_playback) {
38508 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
38509 } else {
38510 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
38511 }
38512
38513 return MA_SUCCESS;
38514 }
38515
38516
38517 /* If we get here it means we are _not_ using the default device. */
38518 foundDevice = MA_FALSE;
38519
38520 fdTemp = ma_open_temp_device__oss();
38521 if (fdTemp == -1) {
38522 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.");
38523 return MA_NO_BACKEND;
38524 }
38525
38526 result = ioctl(fdTemp, SNDCTL_SYSINFO, &si);
38527 if (result != -1) {
38528 int iAudioDevice;
38529 for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
38530 oss_audioinfo ai;
38531 ai.dev = iAudioDevice;
38532 result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai);
38533 if (result != -1) {
38534 if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) {
38535 /* It has the same name, so now just confirm the type. */
38536 if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) ||
38537 (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) {
38538 unsigned int formatMask;
38539
38540 /* ID */
38541 ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1);
38542
38543 /*
38544 The human readable device name should be in the "ai.handle" variable, but it can
38545 sometimes be empty in which case we just fall back to "ai.name" which is less user
38546 friendly, but usually has a value.
38547 */
38548 if (ai.handle[0] != '\0') {
38549 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1);
38550 } else {
38551 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1);
38552 }
38553
38554
38555 pDeviceInfo->nativeDataFormatCount = 0;
38556
38557 if (deviceType == ma_device_type_playback) {
38558 formatMask = ai.oformats;
38559 } else {
38560 formatMask = ai.iformats;
38561 }
38562
38563 if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) {
38564 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo);
38565 }
38566 if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) {
38567 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo);
38568 }
38569 if ((formatMask & AFMT_U8) != 0) {
38570 ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo);
38571 }
38572
38573 foundDevice = MA_TRUE;
38574 break;
38575 }
38576 }
38577 }
38578 }
38579 } else {
38580 close(fdTemp);
38581 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.");
38582 return MA_NO_BACKEND;
38583 }
38584
38585
38586 close(fdTemp);
38587
38588 if (!foundDevice) {
38589 return MA_NO_DEVICE;
38590 }
38591
38592 return MA_SUCCESS;
38593}
38594
38595static ma_result ma_device_uninit__oss(ma_device* pDevice)
38596{
38597 MA_ASSERT(pDevice != NULL);
38598
38599 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
38600 close(pDevice->oss.fdCapture);
38601 }
38602
38603 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
38604 close(pDevice->oss.fdPlayback);
38605 }
38606
38607 return MA_SUCCESS;
38608}
38609
38610static int ma_format_to_oss(ma_format format)
38611{
38612 int ossFormat = AFMT_U8;
38613 switch (format) {
38614 case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
38615 case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
38616 case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
38617 case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
38618 case ma_format_u8:
38619 default: ossFormat = AFMT_U8; break;
38620 }
38621
38622 return ossFormat;
38623}
38624
38625static ma_format ma_format_from_oss(int ossFormat)
38626{
38627 if (ossFormat == AFMT_U8) {
38628 return ma_format_u8;
38629 } else {
38630 if (ma_is_little_endian()) {
38631 switch (ossFormat) {
38632 case AFMT_S16_LE: return ma_format_s16;
38633 case AFMT_S32_LE: return ma_format_s32;
38634 default: return ma_format_unknown;
38635 }
38636 } else {
38637 switch (ossFormat) {
38638 case AFMT_S16_BE: return ma_format_s16;
38639 case AFMT_S32_BE: return ma_format_s32;
38640 default: return ma_format_unknown;
38641 }
38642 }
38643 }
38644
38645 return ma_format_unknown;
38646}
38647
38648static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
38649{
38650 ma_result result;
38651 int ossResult;
38652 int fd;
38653 const ma_device_id* pDeviceID = NULL;
38654 ma_share_mode shareMode;
38655 int ossFormat;
38656 int ossChannels;
38657 int ossSampleRate;
38658 int ossFragment;
38659
38660 MA_ASSERT(pDevice != NULL);
38661 MA_ASSERT(pConfig != NULL);
38662 MA_ASSERT(deviceType != ma_device_type_duplex);
38663
38664 pDeviceID = pDescriptor->pDeviceID;
38665 shareMode = pDescriptor->shareMode;
38666 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. */
38667 ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS;
38668 ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE;
38669
38670 result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd);
38671 if (result != MA_SUCCESS) {
38672 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.");
38673 return result;
38674 }
38675
38676 /*
38677 The OSS documentation is very clear about the order we should be initializing the device's properties:
38678 1) Format
38679 2) Channels
38680 3) Sample rate.
38681 */
38682
38683 /* Format. */
38684 ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat);
38685 if (ossResult == -1) {
38686 close(fd);
38687 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format.");
38688 return ma_result_from_errno(errno);
38689 }
38690
38691 /* Channels. */
38692 ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels);
38693 if (ossResult == -1) {
38694 close(fd);
38695 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count.");
38696 return ma_result_from_errno(errno);
38697 }
38698
38699 /* Sample Rate. */
38700 ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate);
38701 if (ossResult == -1) {
38702 close(fd);
38703 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate.");
38704 return ma_result_from_errno(errno);
38705 }
38706
38707 /*
38708 Buffer.
38709
38710 The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if
38711 it should be done before or after format/channels/rate.
38712
38713 OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual
38714 value.
38715 */
38716 {
38717 ma_uint32 periodSizeInFrames;
38718 ma_uint32 periodSizeInBytes;
38719 ma_uint32 ossFragmentSizePower;
38720
38721 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile);
38722
38723 periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels));
38724 if (periodSizeInBytes < 16) {
38725 periodSizeInBytes = 16;
38726 }
38727
38728 ossFragmentSizePower = 4;
38729 periodSizeInBytes >>= 4;
38730 while (periodSizeInBytes >>= 1) {
38731 ossFragmentSizePower += 1;
38732 }
38733
38734 ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower);
38735 ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment);
38736 if (ossResult == -1) {
38737 close(fd);
38738 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count.");
38739 return ma_result_from_errno(errno);
38740 }
38741 }
38742
38743 /* Internal settings. */
38744 if (deviceType == ma_device_type_capture) {
38745 pDevice->oss.fdCapture = fd;
38746 } else {
38747 pDevice->oss.fdPlayback = fd;
38748 }
38749
38750 pDescriptor->format = ma_format_from_oss(ossFormat);
38751 pDescriptor->channels = ossChannels;
38752 pDescriptor->sampleRate = ossSampleRate;
38753 ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels);
38754 pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16);
38755 pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels);
38756
38757 if (pDescriptor->format == ma_format_unknown) {
38758 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.");
38760 }
38761
38762 return MA_SUCCESS;
38763}
38764
38765static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
38766{
38767 MA_ASSERT(pDevice != NULL);
38768 MA_ASSERT(pConfig != NULL);
38769
38770 MA_ZERO_OBJECT(&pDevice->oss);
38771
38772 if (pConfig->deviceType == ma_device_type_loopback) {
38774 }
38775
38776 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
38777 ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
38778 if (result != MA_SUCCESS) {
38779 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.");
38780 return result;
38781 }
38782 }
38783
38784 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
38785 ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
38786 if (result != MA_SUCCESS) {
38787 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.");
38788 return result;
38789 }
38790 }
38791
38792 return MA_SUCCESS;
38793}
38794
38795/*
38796Note on Starting and Stopping
38797=============================
38798In the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when
38799trying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will
38800fail. Instead what we need to do is just not write or read to and from the device when the
38801device is not running.
38802
38803As a result, both the start and stop functions for OSS are just empty stubs. The starting and
38804stopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check
38805the device state, and if the device is stopped they will simply not do any kind of processing.
38806
38807The downside to this technique is that I've noticed a fairly lengthy delay in stopping the
38808device, up to a second. This is on a virtual machine, and as such might just be due to the
38809virtual drivers, but I'm not fully sure. I am not sure how to work around this problem so for
38810the moment that's just how it's going to have to be.
38811
38812When starting the device, OSS will automatically start it when write() or read() is called.
38813*/
38814static ma_result ma_device_start__oss(ma_device* pDevice)
38815{
38816 MA_ASSERT(pDevice != NULL);
38817
38818 /* The device is automatically started with reading and writing. */
38819 (void)pDevice;
38820
38821 return MA_SUCCESS;
38822}
38823
38824static ma_result ma_device_stop__oss(ma_device* pDevice)
38825{
38826 MA_ASSERT(pDevice != NULL);
38827
38828 /* See note above on why this is empty. */
38829 (void)pDevice;
38830
38831 return MA_SUCCESS;
38832}
38833
38834static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
38835{
38836 int resultOSS;
38837 ma_uint32 deviceState;
38838
38839 if (pFramesWritten != NULL) {
38840 *pFramesWritten = 0;
38841 }
38842
38843 /* Don't do any processing if the device is stopped. */
38844 deviceState = ma_device_get_state(pDevice);
38845 if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) {
38846 return MA_SUCCESS;
38847 }
38848
38849 resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
38850 if (resultOSS < 0) {
38851 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device.");
38852 return ma_result_from_errno(errno);
38853 }
38854
38855 if (pFramesWritten != NULL) {
38856 *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
38857 }
38858
38859 return MA_SUCCESS;
38860}
38861
38862static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
38863{
38864 int resultOSS;
38865 ma_uint32 deviceState;
38866
38867 if (pFramesRead != NULL) {
38868 *pFramesRead = 0;
38869 }
38870
38871 /* Don't do any processing if the device is stopped. */
38872 deviceState = ma_device_get_state(pDevice);
38873 if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) {
38874 return MA_SUCCESS;
38875 }
38876
38877 resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
38878 if (resultOSS < 0) {
38879 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.");
38880 return ma_result_from_errno(errno);
38881 }
38882
38883 if (pFramesRead != NULL) {
38884 *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
38885 }
38886
38887 return MA_SUCCESS;
38888}
38889
38890static ma_result ma_context_uninit__oss(ma_context* pContext)
38891{
38892 MA_ASSERT(pContext != NULL);
38893 MA_ASSERT(pContext->backend == ma_backend_oss);
38894
38895 (void)pContext;
38896 return MA_SUCCESS;
38897}
38898
38899static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
38900{
38901 int fd;
38902 int ossVersion;
38903 int result;
38904
38905 MA_ASSERT(pContext != NULL);
38906
38907 (void)pConfig;
38908
38909 /* Try opening a temporary device first so we can get version information. This is closed at the end. */
38910 fd = ma_open_temp_device__oss();
38911 if (fd == -1) {
38912 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. */
38913 return MA_NO_BACKEND;
38914 }
38915
38916 /* Grab the OSS version. */
38917 ossVersion = 0;
38918 result = ioctl(fd, OSS_GETVERSION, &ossVersion);
38919 if (result == -1) {
38920 close(fd);
38921 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version.");
38922 return MA_NO_BACKEND;
38923 }
38924
38925 /* The file handle to temp device is no longer needed. Close ASAP. */
38926 close(fd);
38927
38928 pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16);
38929 pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8);
38930
38931 pCallbacks->onContextInit = ma_context_init__oss;
38932 pCallbacks->onContextUninit = ma_context_uninit__oss;
38933 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss;
38934 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss;
38935 pCallbacks->onDeviceInit = ma_device_init__oss;
38936 pCallbacks->onDeviceUninit = ma_device_uninit__oss;
38937 pCallbacks->onDeviceStart = ma_device_start__oss;
38938 pCallbacks->onDeviceStop = ma_device_stop__oss;
38939 pCallbacks->onDeviceRead = ma_device_read__oss;
38940 pCallbacks->onDeviceWrite = ma_device_write__oss;
38941 pCallbacks->onDeviceDataLoop = NULL;
38942
38943 return MA_SUCCESS;
38944}
38945#endif /* MA_HAS_OSS */
38946
38947
38948
38949
38950
38951/******************************************************************************
38952
38953AAudio Backend
38954
38955******************************************************************************/
38956#ifdef MA_HAS_AAUDIO
38957
38958#ifdef MA_NO_RUNTIME_LINKING
38959 #include <AAudio/AAudio.h>
38960#endif
38961
38962typedef int32_t ma_aaudio_result_t;
38963typedef int32_t ma_aaudio_direction_t;
38964typedef int32_t ma_aaudio_sharing_mode_t;
38965typedef int32_t ma_aaudio_format_t;
38966typedef int32_t ma_aaudio_stream_state_t;
38967typedef int32_t ma_aaudio_performance_mode_t;
38968typedef int32_t ma_aaudio_usage_t;
38969typedef int32_t ma_aaudio_content_type_t;
38970typedef int32_t ma_aaudio_input_preset_t;
38971typedef int32_t ma_aaudio_allowed_capture_policy_t;
38972typedef int32_t ma_aaudio_data_callback_result_t;
38973typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder;
38974typedef struct ma_AAudioStream_t* ma_AAudioStream;
38975
38976#define MA_AAUDIO_UNSPECIFIED 0
38977
38978/* Result codes. miniaudio only cares about the success code. */
38979#define MA_AAUDIO_OK 0
38980
38981/* Directions. */
38982#define MA_AAUDIO_DIRECTION_OUTPUT 0
38983#define MA_AAUDIO_DIRECTION_INPUT 1
38984
38985/* Sharing modes. */
38986#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0
38987#define MA_AAUDIO_SHARING_MODE_SHARED 1
38988
38989/* Formats. */
38990#define MA_AAUDIO_FORMAT_PCM_I16 1
38991#define MA_AAUDIO_FORMAT_PCM_FLOAT 2
38992
38993/* Stream states. */
38994#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0
38995#define MA_AAUDIO_STREAM_STATE_UNKNOWN 1
38996#define MA_AAUDIO_STREAM_STATE_OPEN 2
38997#define MA_AAUDIO_STREAM_STATE_STARTING 3
38998#define MA_AAUDIO_STREAM_STATE_STARTED 4
38999#define MA_AAUDIO_STREAM_STATE_PAUSING 5
39000#define MA_AAUDIO_STREAM_STATE_PAUSED 6
39001#define MA_AAUDIO_STREAM_STATE_FLUSHING 7
39002#define MA_AAUDIO_STREAM_STATE_FLUSHED 8
39003#define MA_AAUDIO_STREAM_STATE_STOPPING 9
39004#define MA_AAUDIO_STREAM_STATE_STOPPED 10
39005#define MA_AAUDIO_STREAM_STATE_CLOSING 11
39006#define MA_AAUDIO_STREAM_STATE_CLOSED 12
39007#define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13
39008
39009/* Performance modes. */
39010#define MA_AAUDIO_PERFORMANCE_MODE_NONE 10
39011#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11
39012#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12
39013
39014/* Usage types. */
39015#define MA_AAUDIO_USAGE_MEDIA 1
39016#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2
39017#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3
39018#define MA_AAUDIO_USAGE_ALARM 4
39019#define MA_AAUDIO_USAGE_NOTIFICATION 5
39020#define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6
39021#define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10
39022#define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11
39023#define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12
39024#define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13
39025#define MA_AAUDIO_USAGE_GAME 14
39026#define MA_AAUDIO_USAGE_ASSISTANT 16
39027#define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000
39028#define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001
39029#define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002
39030#define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003
39031
39032/* Content types. */
39033#define MA_AAUDIO_CONTENT_TYPE_SPEECH 1
39034#define MA_AAUDIO_CONTENT_TYPE_MUSIC 2
39035#define MA_AAUDIO_CONTENT_TYPE_MOVIE 3
39036#define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4
39037
39038/* Input presets. */
39039#define MA_AAUDIO_INPUT_PRESET_GENERIC 1
39040#define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5
39041#define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6
39042#define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7
39043#define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9
39044#define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10
39045
39046/* Allowed Capture Policies */
39047#define MA_AAUDIO_ALLOW_CAPTURE_BY_ALL 1
39048#define MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM 2
39049#define MA_AAUDIO_ALLOW_CAPTURE_BY_NONE 3
39050
39051/* Callback results. */
39052#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0
39053#define MA_AAUDIO_CALLBACK_RESULT_STOP 1
39054
39055
39056typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames);
39057typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error);
39058
39059typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder);
39060typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder);
39061typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId);
39062typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction);
39063typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode);
39064typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format);
39065typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount);
39066typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate);
39067typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
39068typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
39069typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData);
39070typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData);
39071typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode);
39072typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType);
39073typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType);
39074typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset);
39075typedef void (* MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_allowed_capture_policy_t policy);
39076typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream);
39077typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream);
39078typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream);
39079typedef 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);
39080typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream);
39081typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream);
39082typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream);
39083typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream);
39084typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream);
39085typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream);
39086typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream);
39087typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream);
39088
39089static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA)
39090{
39091 switch (resultAA)
39092 {
39093 case MA_AAUDIO_OK: return MA_SUCCESS;
39094 default: break;
39095 }
39096
39097 return MA_ERROR;
39098}
39099
39100static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage)
39101{
39102 switch (usage) {
39103 case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_MEDIA;
39104 case ma_aaudio_usage_voice_communication: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION;
39105 case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
39106 case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_ALARM;
39107 case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_NOTIFICATION;
39108 case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE;
39109 case ma_aaudio_usage_notification_event: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT;
39110 case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
39111 case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
39112 case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION;
39113 case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_GAME;
39114 case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANT;
39115 case ma_aaudio_usage_emergency: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY;
39116 case ma_aaudio_usage_safety: return MA_AAUDIO_SYSTEM_USAGE_SAFETY;
39117 case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS;
39118 case ma_aaudio_usage_announcement: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT;
39119 default: break;
39120 }
39121
39122 return MA_AAUDIO_USAGE_MEDIA;
39123}
39124
39125static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType)
39126{
39127 switch (contentType) {
39128 case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH;
39129 case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC;
39130 case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE;
39131 case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION;
39132 default: break;
39133 }
39134
39135 return MA_AAUDIO_CONTENT_TYPE_SPEECH;
39136}
39137
39138static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset)
39139{
39140 switch (inputPreset) {
39141 case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC;
39142 case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER;
39143 case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION;
39144 case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION;
39145 case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED;
39146 case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE;
39147 default: break;
39148 }
39149
39150 return MA_AAUDIO_INPUT_PRESET_GENERIC;
39151}
39152
39153static ma_aaudio_allowed_capture_policy_t ma_to_allowed_capture_policy__aaudio(ma_aaudio_allowed_capture_policy allowedCapturePolicy)
39154{
39155 switch (allowedCapturePolicy) {
39156 case ma_aaudio_allow_capture_by_all: return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL;
39157 case ma_aaudio_allow_capture_by_system: return MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM;
39158 case ma_aaudio_allow_capture_by_none: return MA_AAUDIO_ALLOW_CAPTURE_BY_NONE;
39159 default: break;
39160 }
39161
39162 return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL;
39163}
39164
39165static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error)
39166{
39167 ma_result result;
39168 ma_job job;
39169 ma_device* pDevice = (ma_device*)pUserData;
39170 MA_ASSERT(pDevice != NULL);
39171
39172 (void)error;
39173 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));
39174
39175 /*
39176 When we get an error, we'll assume that the stream is in an erroneous state and needs to be restarted. From the documentation,
39177 we cannot do this from the error callback. Therefore we are going to use an event thread for the AAudio backend to do this
39178 cleanly and safely.
39179 */
39180 if (ma_atomic_bool32_get(&pDevice->aaudio.isTearingDown)) {
39181 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Tearing down device.\n");
39182 }
39183 else {
39185 job.data.device.aaudio.reroute.pDevice = pDevice;
39186
39187 if (pStream == pDevice->aaudio.pStreamCapture) {
39189 } else {
39191 }
39192
39193 result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job);
39194 if (result != MA_SUCCESS) {
39195 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n");
39196 return;
39197 }
39198 }
39199}
39200
39201static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
39202{
39203 ma_device* pDevice = (ma_device*)pUserData;
39204 MA_ASSERT(pDevice != NULL);
39205
39206 if (frameCount > 0) {
39207 ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, (ma_uint32)frameCount);
39208 }
39209
39210 (void)pStream;
39211 return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
39212}
39213
39214static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
39215{
39216 ma_device* pDevice = (ma_device*)pUserData;
39217 MA_ASSERT(pDevice != NULL);
39218
39219 /*
39220 I've had a report that AAudio can sometimes post a frame count of 0. We need to check for that here
39221 so we don't get any errors at a deeper level. I'm doing the same with the capture side for safety,
39222 though I've not yet had any reports about that one.
39223 */
39224 if (frameCount > 0) {
39225 ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, (ma_uint32)frameCount);
39226 }
39227
39228 (void)pStream;
39229 return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
39230}
39231
39232static 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)
39233{
39234 ma_AAudioStreamBuilder* pBuilder;
39235 ma_aaudio_result_t resultAA;
39236
39237 /* Safety. */
39238 *ppBuilder = NULL;
39239
39240 resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder);
39241 if (resultAA != MA_AAUDIO_OK) {
39242 return ma_result_from_aaudio(resultAA);
39243 }
39244
39245 if (pDeviceID != NULL) {
39246 ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio);
39247 }
39248
39249 ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT);
39250 ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE);
39251
39252
39253 /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */
39254 if (pDescriptor != NULL) {
39255 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. */
39256
39257 if (pDescriptor->sampleRate != 0) {
39258 ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate);
39259 }
39260
39261 if (pDescriptor->channels != 0) {
39262 ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
39263 }
39264
39265 if (pDescriptor->format != ma_format_unknown) {
39266 ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
39267 }
39268
39269
39270 /*
39271 There have been reports where setting the frames per data callback results in an error.
39272 In particular, re-routing may inadvertently switch from low-latency mode, resulting in a less stable
39273 stream from the legacy path (AudioStreamLegacy). To address this, we simply don't set the value. It
39274 can still be set if it's explicitly requested via the aaudio.allowSetBufferCapacity variable in the
39275 device config.
39276 */
39277 if ((!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) && pConfig->aaudio.allowSetBufferCapacity) {
39278 /*
39279 AAudio is annoying when it comes to its buffer calculation stuff because it doesn't let you
39280 retrieve the actual sample rate until after you've opened the stream. But you need to configure
39281 the buffer capacity before you open the stream... :/
39282
39283 To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on.
39284 */
39285 ma_uint32 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount;
39286
39287 ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames);
39288 ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount);
39289 }
39290
39291 if (deviceType == ma_device_type_capture) {
39292 if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) {
39293 ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset));
39294 }
39295
39296 ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice);
39297 } else {
39298 if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) {
39299 ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage));
39300 }
39301
39302 if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) {
39303 ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType));
39304 }
39305
39306 if (pConfig->aaudio.allowedCapturePolicy != ma_aaudio_allow_capture_default && pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy != NULL) {
39307 ((MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy)pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy)(pBuilder, ma_to_allowed_capture_policy__aaudio(pConfig->aaudio.allowedCapturePolicy));
39308 }
39309
39310 ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice);
39311 }
39312
39313 /*
39314 If we set AAUDIO_PERFORMANCE_MODE_LOW_LATENCY, we allow for MMAP (non-legacy path).
39315 Since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let's use it.
39316 Beware though, with a conservative performance profile, AAudio will indeed take the legacy path.
39317 */
39318 ((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);
39319
39320 /* We need to set an error callback to detect device changes. */
39321 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. */
39322 ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice);
39323 }
39324 }
39325
39326 *ppBuilder = pBuilder;
39327
39328 return MA_SUCCESS;
39329}
39330
39331static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream)
39332{
39333 ma_result result;
39334
39335 result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream));
39336 ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
39337
39338 return result;
39339}
39340
39341static 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)
39342{
39343 ma_result result;
39344 ma_AAudioStreamBuilder* pBuilder;
39345
39346 *ppStream = NULL;
39347
39348 result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder);
39349 if (result != MA_SUCCESS) {
39350 return result;
39351 }
39352
39353 /* Let's give AAudio a hint to avoid the legacy path (AudioStreamLegacy). */
39354 ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
39355
39356 return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream);
39357}
39358
39359static 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)
39360{
39361 ma_result result;
39362 ma_AAudioStreamBuilder* pBuilder;
39363
39364 MA_ASSERT(pDevice != NULL);
39365 MA_ASSERT(pDescriptor != NULL);
39366 MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */
39367
39368 *ppStream = NULL;
39369
39370 result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder);
39371 if (result != MA_SUCCESS) {
39372 return result;
39373 }
39374
39375 return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream);
39376}
39377
39378static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream)
39379{
39380 if (pStream == NULL) {
39381 return MA_INVALID_ARGS;
39382 }
39383
39384 return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream));
39385}
39386
39387static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType)
39388{
39389 /* The only way to know this is to try creating a stream. */
39390 ma_AAudioStream* pStream;
39391 ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream);
39392 if (result != MA_SUCCESS) {
39393 return MA_FALSE;
39394 }
39395
39396 ma_close_stream__aaudio(pContext, pStream);
39397 return MA_TRUE;
39398}
39399
39400static 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)
39401{
39402 ma_aaudio_stream_state_t actualNewState;
39403 ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */
39404 if (resultAA != MA_AAUDIO_OK) {
39405 return ma_result_from_aaudio(resultAA);
39406 }
39407
39408 if (newState != actualNewState) {
39409 return MA_ERROR; /* Failed to transition into the expected state. */
39410 }
39411
39412 return MA_SUCCESS;
39413}
39414
39415
39416static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
39417{
39418 ma_bool32 cbResult = MA_TRUE;
39419
39420 MA_ASSERT(pContext != NULL);
39421 MA_ASSERT(callback != NULL);
39422
39423 /* 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. */
39424
39425 /* Playback. */
39426 if (cbResult) {
39427 ma_device_info deviceInfo;
39428 MA_ZERO_OBJECT(&deviceInfo);
39429 deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
39430 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
39431
39432 if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) {
39433 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
39434 }
39435 }
39436
39437 /* Capture. */
39438 if (cbResult) {
39439 ma_device_info deviceInfo;
39440 MA_ZERO_OBJECT(&deviceInfo);
39441 deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
39442 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
39443
39444 if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) {
39445 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
39446 }
39447 }
39448
39449 return MA_SUCCESS;
39450}
39451
39452static 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)
39453{
39454 MA_ASSERT(pContext != NULL);
39455 MA_ASSERT(pStream != NULL);
39456 MA_ASSERT(pDeviceInfo != NULL);
39457
39458 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
39459 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream);
39460 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream);
39461 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
39462 pDeviceInfo->nativeDataFormatCount += 1;
39463}
39464
39465static void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo)
39466{
39467 /* AAudio supports s16 and f32. */
39468 ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo);
39469 ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo);
39470}
39471
39472static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
39473{
39474 ma_AAudioStream* pStream;
39475 ma_result result;
39476
39477 MA_ASSERT(pContext != NULL);
39478
39479 /* ID */
39480 if (pDeviceID != NULL) {
39481 pDeviceInfo->id.aaudio = pDeviceID->aaudio;
39482 } else {
39483 pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED;
39484 }
39485
39486 /* Name */
39487 if (deviceType == ma_device_type_playback) {
39488 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
39489 } else {
39490 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
39491 }
39492
39493
39494 pDeviceInfo->nativeDataFormatCount = 0;
39495
39496 /* We'll need to open the device to get accurate sample rate and channel count information. */
39497 result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream);
39498 if (result != MA_SUCCESS) {
39499 return result;
39500 }
39501
39502 ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo);
39503
39504 ma_close_stream__aaudio(pContext, pStream);
39505 pStream = NULL;
39506
39507 return MA_SUCCESS;
39508}
39509
39510static ma_result ma_close_streams__aaudio(ma_device* pDevice)
39511{
39512 MA_ASSERT(pDevice != NULL);
39513
39514 /* When rerouting, streams may have been closed and never re-opened. Hence the extra checks below. */
39515 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
39516 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
39517 pDevice->aaudio.pStreamCapture = NULL;
39518 }
39519 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
39520 ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
39521 pDevice->aaudio.pStreamPlayback = NULL;
39522 }
39523
39524 return MA_SUCCESS;
39525}
39526
39527static ma_result ma_device_uninit__aaudio(ma_device* pDevice)
39528{
39529 MA_ASSERT(pDevice != NULL);
39530
39531 /*
39532 Note: Closing the streams may cause a timeout error, which would then trigger rerouting in our error callback.
39533 We must not schedule a reroute when device is getting destroyed.
39534 */
39535 ma_atomic_bool32_set(&pDevice->aaudio.isTearingDown, MA_TRUE);
39536
39537 /* Wait for any rerouting to finish before attempting to close the streams. */
39538 ma_mutex_lock(&pDevice->aaudio.rerouteLock);
39539 {
39540 ma_close_streams__aaudio(pDevice);
39541 }
39542 ma_mutex_unlock(&pDevice->aaudio.rerouteLock);
39543
39544 /* Destroy rerouting lock. */
39545 ma_mutex_uninit(&pDevice->aaudio.rerouteLock);
39546
39547 return MA_SUCCESS;
39548}
39549
39550static 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)
39551{
39552 ma_result result;
39553 int32_t bufferCapacityInFrames;
39554 int32_t framesPerDataCallback;
39555 ma_AAudioStream* pStream;
39556
39557 MA_ASSERT(pDevice != NULL);
39558 MA_ASSERT(pConfig != NULL);
39559 MA_ASSERT(pDescriptor != NULL);
39560
39561 *ppStream = NULL; /* Safety. */
39562
39563 /* First step is to open the stream. From there we'll be able to extract the internal configuration. */
39564 result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream);
39565 if (result != MA_SUCCESS) {
39566 return result; /* Failed to open the AAudio stream. */
39567 }
39568
39569 /* Now extract the internal configuration. */
39570 pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32;
39571 pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream);
39572 pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream);
39573
39574 /* For the channel map we need to be sure we don't overflow any buffers. */
39575 if (pDescriptor->channels <= MA_MAX_CHANNELS) {
39576 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. */
39577 } else {
39578 ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS); /* Too many channels. Use a blank channel map. */
39579 }
39580
39581 bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream);
39582 framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream);
39583
39584 if (framesPerDataCallback > 0) {
39585 pDescriptor->periodSizeInFrames = framesPerDataCallback;
39586 pDescriptor->periodCount = bufferCapacityInFrames / framesPerDataCallback;
39587 } else {
39588 pDescriptor->periodSizeInFrames = bufferCapacityInFrames;
39589 pDescriptor->periodCount = 1;
39590 }
39591
39592 *ppStream = pStream;
39593
39594 return MA_SUCCESS;
39595}
39596
39597static ma_result ma_device_init_streams__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
39598{
39599 ma_result result;
39600
39601 MA_ASSERT(pDevice != NULL);
39602
39603 if (pConfig->deviceType == ma_device_type_loopback) {
39605 }
39606
39607 pDevice->aaudio.usage = pConfig->aaudio.usage;
39608 pDevice->aaudio.contentType = pConfig->aaudio.contentType;
39609 pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset;
39610 pDevice->aaudio.allowedCapturePolicy = pConfig->aaudio.allowedCapturePolicy;
39611 pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute;
39612
39613 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
39614 result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture);
39615 if (result != MA_SUCCESS) {
39616 return result;
39617 }
39618 }
39619
39620 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
39621 result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback);
39622 if (result != MA_SUCCESS) {
39623 return result;
39624 }
39625 }
39626
39627 return MA_SUCCESS;
39628}
39629
39630static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
39631{
39632 ma_result result;
39633
39634 MA_ASSERT(pDevice != NULL);
39635
39636 result = ma_device_init_streams__aaudio(pDevice, pConfig, pDescriptorPlayback, pDescriptorCapture);
39637 if (result != MA_SUCCESS) {
39638 return result;
39639 }
39640
39641 result = ma_mutex_init(&pDevice->aaudio.rerouteLock);
39642 if (result != MA_SUCCESS) {
39643 return result;
39644 }
39645
39646 return MA_SUCCESS;
39647}
39648
39649static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
39650{
39651 ma_aaudio_result_t resultAA;
39652 ma_aaudio_stream_state_t currentState;
39653
39654 MA_ASSERT(pDevice != NULL);
39655
39656 if (pStream == NULL) {
39657 return MA_INVALID_ARGS;
39658 }
39659
39660 resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream);
39661 if (resultAA != MA_AAUDIO_OK) {
39662 return ma_result_from_aaudio(resultAA);
39663 }
39664
39665 /* Do we actually need to wait for the device to transition into its started state? */
39666
39667 /* 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. */
39668 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
39669 if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) {
39670 ma_result result;
39671
39672 if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) {
39673 return MA_ERROR; /* Expecting the stream to be a starting or started state. */
39674 }
39675
39676 result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED);
39677 if (result != MA_SUCCESS) {
39678 return result;
39679 }
39680 }
39681
39682 return MA_SUCCESS;
39683}
39684
39685static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
39686{
39687 ma_aaudio_result_t resultAA;
39688 ma_aaudio_stream_state_t currentState;
39689
39690 MA_ASSERT(pDevice != NULL);
39691
39692 if (pStream == NULL) {
39693 return MA_INVALID_ARGS;
39694 }
39695
39696 /*
39697 From the AAudio documentation:
39698
39699 The stream will stop after all of the data currently buffered has been played.
39700
39701 This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic.
39702 */
39703 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
39704 if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) {
39705 return MA_SUCCESS; /* The device is disconnected. Don't try stopping it. */
39706 }
39707
39708 resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream);
39709 if (resultAA != MA_AAUDIO_OK) {
39710 return ma_result_from_aaudio(resultAA);
39711 }
39712
39713 /* 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. */
39714 currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
39715 if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) {
39716 ma_result result;
39717
39718 if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) {
39719 return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */
39720 }
39721
39722 result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED);
39723 if (result != MA_SUCCESS) {
39724 return result;
39725 }
39726 }
39727
39728 return MA_SUCCESS;
39729}
39730
39731static ma_result ma_device_start__aaudio(ma_device* pDevice)
39732{
39733 MA_ASSERT(pDevice != NULL);
39734
39735 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
39736 ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
39737 if (result != MA_SUCCESS) {
39738 return result;
39739 }
39740 }
39741
39742 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
39743 ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
39744 if (result != MA_SUCCESS) {
39745 if (pDevice->type == ma_device_type_duplex) {
39746 ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
39747 }
39748 return result;
39749 }
39750 }
39751
39752 return MA_SUCCESS;
39753}
39754
39755static ma_result ma_device_stop__aaudio(ma_device* pDevice)
39756{
39757 MA_ASSERT(pDevice != NULL);
39758
39759 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
39760 ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
39761 if (result != MA_SUCCESS) {
39762 return result;
39763 }
39764 }
39765
39766 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
39767 ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
39768 if (result != MA_SUCCESS) {
39769 return result;
39770 }
39771 }
39772
39773 ma_device__on_notification_stopped(pDevice);
39774
39775 return MA_SUCCESS;
39776}
39777
39778static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType)
39779{
39780 const ma_int32 maxAttempts = 4; /* Reasonable retry limit. */
39781
39782 ma_result result;
39783 ma_int32 iAttempt;
39784
39785 MA_ASSERT(pDevice != NULL);
39786
39787 /* We got disconnected! Retry a few times, until we find a connected device! */
39788 iAttempt = 0;
39789 while (iAttempt++ < maxAttempts) {
39790 /* Device tearing down? No need to reroute! */
39791 if (ma_atomic_bool32_get(&pDevice->aaudio.isTearingDown)) {
39792 result = MA_SUCCESS; /* Caller should continue as normal. */
39793 break;
39794 }
39795
39796 /* The first thing to do is close the streams. */
39797 ma_close_streams__aaudio(pDevice);
39798
39799 /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */
39800 ma_device_config deviceConfig;
39801 ma_device_descriptor descriptorPlayback;
39802 ma_device_descriptor descriptorCapture;
39803
39804 deviceConfig = ma_device_config_init(deviceType);
39805 deviceConfig.playback.pDeviceID = NULL; /* Only doing rerouting with default devices. */
39806 deviceConfig.playback.shareMode = pDevice->playback.shareMode;
39807 deviceConfig.playback.format = pDevice->playback.format;
39808 deviceConfig.playback.channels = pDevice->playback.channels;
39809 deviceConfig.capture.pDeviceID = NULL; /* Only doing rerouting with default devices. */
39810 deviceConfig.capture.shareMode = pDevice->capture.shareMode;
39811 deviceConfig.capture.format = pDevice->capture.format;
39812 deviceConfig.capture.channels = pDevice->capture.channels;
39813 deviceConfig.sampleRate = pDevice->sampleRate;
39814 deviceConfig.aaudio.usage = pDevice->aaudio.usage;
39815 deviceConfig.aaudio.contentType = pDevice->aaudio.contentType;
39816 deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset;
39817 deviceConfig.aaudio.allowedCapturePolicy = pDevice->aaudio.allowedCapturePolicy;
39818 deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute;
39819 deviceConfig.periods = 1;
39820
39821 /* Try to get an accurate period size. */
39822 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
39824 } else {
39826 }
39827
39828 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
39829 descriptorCapture.pDeviceID = deviceConfig.capture.pDeviceID;
39830 descriptorCapture.shareMode = deviceConfig.capture.shareMode;
39831 descriptorCapture.format = deviceConfig.capture.format;
39832 descriptorCapture.channels = deviceConfig.capture.channels;
39833 descriptorCapture.sampleRate = deviceConfig.sampleRate;
39834 descriptorCapture.periodSizeInFrames = deviceConfig.periodSizeInFrames;
39835 descriptorCapture.periodCount = deviceConfig.periods;
39836 }
39837
39838 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
39839 descriptorPlayback.pDeviceID = deviceConfig.playback.pDeviceID;
39840 descriptorPlayback.shareMode = deviceConfig.playback.shareMode;
39841 descriptorPlayback.format = deviceConfig.playback.format;
39842 descriptorPlayback.channels = deviceConfig.playback.channels;
39843 descriptorPlayback.sampleRate = deviceConfig.sampleRate;
39844 descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames;
39845 descriptorPlayback.periodCount = deviceConfig.periods;
39846 }
39847
39848 result = ma_device_init_streams__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture);
39849 if (result != MA_SUCCESS) {
39850 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to create stream after route change.");
39851 /* Reroute failed! */
39852 break;
39853 }
39854
39855 result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture);
39856 if (result != MA_SUCCESS) {
39857 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to initialize device after route change.");
39858 ma_close_streams__aaudio(pDevice);
39859 /* Reroute failed! */
39860 break;
39861 }
39862
39863 /* We'll only ever do this in response to a reroute. */
39864 ma_device__on_notification_rerouted(pDevice);
39865
39866 /* If the device is started, start the streams. Maybe make this configurable? */
39868 if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) {
39869 result = ma_device_start__aaudio(pDevice);
39870 if (result != MA_SUCCESS) {
39871 if (iAttempt < maxAttempts) {
39872 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, retrying(%d)", iAttempt);
39873 } else {
39874 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, giving up.");
39875 }
39876 }
39877 } else {
39878 ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */
39879 }
39880 }
39881
39882 if (result == MA_SUCCESS) {
39883 /* Reroute successful! */
39884 break;
39885 }
39886 }
39887
39888 return result;
39889}
39890
39891static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo)
39892{
39893 ma_AAudioStream* pStream = NULL;
39894
39895 MA_ASSERT(pDevice != NULL);
39896 MA_ASSERT(type != ma_device_type_duplex);
39897 MA_ASSERT(pDeviceInfo != NULL);
39898
39899 if (type == ma_device_type_capture) {
39900 pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture;
39901 pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio;
39902 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */
39903 }
39904 if (type == ma_device_type_playback) {
39905 pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback;
39906 pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio;
39907 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */
39908 }
39909
39910 /* Safety. Should never happen. */
39911 if (pStream == NULL) {
39912 return MA_INVALID_OPERATION;
39913 }
39914
39915 pDeviceInfo->nativeDataFormatCount = 0;
39916 ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo);
39917
39918 return MA_SUCCESS;
39919}
39920
39921
39922static ma_result ma_context_uninit__aaudio(ma_context* pContext)
39923{
39924 MA_ASSERT(pContext != NULL);
39925 MA_ASSERT(pContext->backend == ma_backend_aaudio);
39926
39927 ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks);
39928
39929 ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio);
39930 pContext->aaudio.hAAudio = NULL;
39931
39932 return MA_SUCCESS;
39933}
39934
39935static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
39936{
39937#if !defined(MA_NO_RUNTIME_LINKING)
39938 size_t i;
39939 const char* libNames[] = {
39940 "libaaudio.so"
39941 };
39942
39943 for (i = 0; i < ma_countof(libNames); ++i) {
39944 pContext->aaudio.hAAudio = ma_dlopen(ma_context_get_log(pContext), libNames[i]);
39945 if (pContext->aaudio.hAAudio != NULL) {
39946 break;
39947 }
39948 }
39949
39950 if (pContext->aaudio.hAAudio == NULL) {
39951 return MA_FAILED_TO_INIT_BACKEND;
39952 }
39953
39954 pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudio_createStreamBuilder");
39955 pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete");
39956 pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId");
39957 pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection");
39958 pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode");
39959 pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat");
39960 pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount");
39961 pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate");
39962 pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames");
39963 pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback");
39964 pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback");
39965 pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback");
39966 pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode");
39967 pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage");
39968 pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType");
39969 pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset");
39970 pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy");
39971 pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream");
39972 pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_close");
39973 pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getState");
39974 pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange");
39975 pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFormat");
39976 pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getChannelCount");
39977 pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getSampleRate");
39978 pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames");
39979 pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback");
39980 pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst");
39981 pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStart");
39982 pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStop");
39983#else
39984 pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)AAudio_createStreamBuilder;
39985 pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)AAudioStreamBuilder_delete;
39986 pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)AAudioStreamBuilder_setDeviceId;
39987 pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)AAudioStreamBuilder_setDirection;
39988 pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)AAudioStreamBuilder_setSharingMode;
39989 pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)AAudioStreamBuilder_setFormat;
39990 pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)AAudioStreamBuilder_setChannelCount;
39991 pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)AAudioStreamBuilder_setSampleRate;
39992 pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)AAudioStreamBuilder_setBufferCapacityInFrames;
39993 pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)AAudioStreamBuilder_setFramesPerDataCallback;
39994 pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)AAudioStreamBuilder_setDataCallback;
39995 pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)AAudioStreamBuilder_setErrorCallback;
39996 pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)AAudioStreamBuilder_setPerformanceMode;
39997 pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)AAudioStreamBuilder_setUsage;
39998 pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)AAudioStreamBuilder_setContentType;
39999 pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)AAudioStreamBuilder_setInputPreset;
40000 #if defined(__ANDROID_API__) && __ANDROID_API__ >= 29
40001 pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)AAudioStreamBuilder_setAllowedCapturePolicy;
40002 #endif
40003 pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)AAudioStreamBuilder_openStream;
40004 pContext->aaudio.AAudioStream_close = (ma_proc)AAudioStream_close;
40005 pContext->aaudio.AAudioStream_getState = (ma_proc)AAudioStream_getState;
40006 pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)AAudioStream_waitForStateChange;
40007 pContext->aaudio.AAudioStream_getFormat = (ma_proc)AAudioStream_getFormat;
40008 pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)AAudioStream_getChannelCount;
40009 pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)AAudioStream_getSampleRate;
40010 pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)AAudioStream_getBufferCapacityInFrames;
40011 pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)AAudioStream_getFramesPerDataCallback;
40012 pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)AAudioStream_getFramesPerBurst;
40013 pContext->aaudio.AAudioStream_requestStart = (ma_proc)AAudioStream_requestStart;
40014 pContext->aaudio.AAudioStream_requestStop = (ma_proc)AAudioStream_requestStop;
40015#endif
40016
40017 pCallbacks->onContextInit = ma_context_init__aaudio;
40018 pCallbacks->onContextUninit = ma_context_uninit__aaudio;
40019 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio;
40020 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio;
40021 pCallbacks->onDeviceInit = ma_device_init__aaudio;
40022 pCallbacks->onDeviceUninit = ma_device_uninit__aaudio;
40023 pCallbacks->onDeviceStart = ma_device_start__aaudio;
40024 pCallbacks->onDeviceStop = ma_device_stop__aaudio;
40025 pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */
40026 pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */
40027 pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */
40028 pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio;
40029
40030
40031 /* We need a job thread so we can deal with rerouting. */
40032 {
40033 ma_result result;
40034 ma_device_job_thread_config jobThreadConfig;
40035
40036 jobThreadConfig = ma_device_job_thread_config_init();
40037
40038 result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread);
40039 if (result != MA_SUCCESS) {
40040 ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio);
40041 pContext->aaudio.hAAudio = NULL;
40042 return result;
40043 }
40044 }
40045
40046
40047 (void)pConfig;
40048 return MA_SUCCESS;
40049}
40050
40051static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)
40052{
40053 ma_result result = MA_SUCCESS;
40054 ma_device* pDevice;
40055
40056 MA_ASSERT(pJob != NULL);
40057
40058 pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice;
40059 MA_ASSERT(pDevice != NULL);
40060
40061 ma_mutex_lock(&pDevice->aaudio.rerouteLock);
40062 {
40063 /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */
40064 result = ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType);
40065 if (result != MA_SUCCESS) {
40066 /*
40067 Getting here means we failed to reroute the device. The best thing I can think of here is to
40068 just stop the device.
40069 */
40070 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[AAudio] Stopping device due to reroute failure.");
40071 ma_device_stop(pDevice);
40072 }
40073 }
40074 ma_mutex_unlock(&pDevice->aaudio.rerouteLock);
40075
40076 return result;
40077}
40078#else
40079/* Getting here means there is no AAudio backend so we need a no-op job implementation. */
40080static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)
40081{
40082 return ma_job_process__noop(pJob);
40083}
40084#endif /* AAudio */
40085
40086
40087/******************************************************************************
40088
40089OpenSL|ES Backend
40090
40091******************************************************************************/
40092#ifdef MA_HAS_OPENSL
40093#include <SLES/OpenSLES.h>
40094#ifdef MA_ANDROID
40095#include <SLES/OpenSLES_Android.h>
40096#endif
40097
40098typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired);
40099
40100/* OpenSL|ES has one-per-application objects :( */
40101static SLObjectItf g_maEngineObjectSL = NULL;
40102static SLEngineItf g_maEngineSL = NULL;
40103static ma_uint32 g_maOpenSLInitCounter = 0;
40104static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */
40105
40106#define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p)))
40107#define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p)))
40108#define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p)))
40109#define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p)))
40110
40111#ifdef MA_ANDROID
40112#define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p)))
40113#else
40114#define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p)))
40115#endif
40116
40117static ma_result ma_result_from_OpenSL(SLuint32 result)
40118{
40119 switch (result)
40120 {
40121 case SL_RESULT_SUCCESS: return MA_SUCCESS;
40122 case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR;
40123 case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS;
40124 case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY;
40125 case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA;
40126 case SL_RESULT_RESOURCE_LOST: return MA_ERROR;
40127 case SL_RESULT_IO_ERROR: return MA_IO_ERROR;
40128 case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE;
40129 case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA;
40130 case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED;
40131 case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR;
40132 case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED;
40133 case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED;
40134 case SL_RESULT_INTERNAL_ERROR: return MA_ERROR;
40135 case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR;
40136 case SL_RESULT_OPERATION_ABORTED: return MA_ERROR;
40137 case SL_RESULT_CONTROL_LOST: return MA_ERROR;
40138 default: return MA_ERROR;
40139 }
40140}
40141
40142/* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
40143static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id)
40144{
40145 switch (id)
40146 {
40147 case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
40148 case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
40149 case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
40150 case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
40151 case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
40152 case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
40153 case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
40154 case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
40155 case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
40156 case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
40157 case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
40158 case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
40159 case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
40160 case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
40161 case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
40162 case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
40163 case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
40164 case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
40165 default: return 0;
40166 }
40167}
40168
40169/* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */
40170static SLuint32 ma_channel_id_to_opensl(ma_uint8 id)
40171{
40172 switch (id)
40173 {
40174 case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER;
40175 case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT;
40176 case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT;
40177 case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER;
40178 case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY;
40179 case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT;
40180 case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT;
40181 case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER;
40182 case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER;
40183 case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER;
40184 case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT;
40185 case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT;
40186 case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER;
40187 case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT;
40188 case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER;
40189 case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT;
40190 case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT;
40191 case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER;
40192 case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT;
40193 default: return 0;
40194 }
40195}
40196
40197/* Converts a channel mapping to an OpenSL-style channel mask. */
40198static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels)
40199{
40200 SLuint32 channelMask = 0;
40201 ma_uint32 iChannel;
40202 for (iChannel = 0; iChannel < channels; ++iChannel) {
40203 channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]);
40204 }
40205
40206 return channelMask;
40207}
40208
40209/* Converts an OpenSL-style channel mask to a miniaudio channel map. */
40210static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap)
40211{
40212 if (channels == 1 && channelMask == 0) {
40213 pChannelMap[0] = MA_CHANNEL_MONO;
40214 } else if (channels == 2 && channelMask == 0) {
40215 pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
40216 pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
40217 } else {
40218 if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) {
40219 pChannelMap[0] = MA_CHANNEL_MONO;
40220 } else {
40221 /* Just iterate over each bit. */
40222 ma_uint32 iChannel = 0;
40223 ma_uint32 iBit;
40224 for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {
40225 SLuint32 bitValue = (channelMask & (1UL << iBit));
40226 if (bitValue != 0) {
40227 /* The bit is set. */
40228 pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue);
40229 iChannel += 1;
40230 }
40231 }
40232 }
40233 }
40234}
40235
40236static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec)
40237{
40238 if (samplesPerSec <= SL_SAMPLINGRATE_8) {
40239 return SL_SAMPLINGRATE_8;
40240 }
40241 if (samplesPerSec <= SL_SAMPLINGRATE_11_025) {
40242 return SL_SAMPLINGRATE_11_025;
40243 }
40244 if (samplesPerSec <= SL_SAMPLINGRATE_12) {
40245 return SL_SAMPLINGRATE_12;
40246 }
40247 if (samplesPerSec <= SL_SAMPLINGRATE_16) {
40248 return SL_SAMPLINGRATE_16;
40249 }
40250 if (samplesPerSec <= SL_SAMPLINGRATE_22_05) {
40251 return SL_SAMPLINGRATE_22_05;
40252 }
40253 if (samplesPerSec <= SL_SAMPLINGRATE_24) {
40254 return SL_SAMPLINGRATE_24;
40255 }
40256 if (samplesPerSec <= SL_SAMPLINGRATE_32) {
40257 return SL_SAMPLINGRATE_32;
40258 }
40259 if (samplesPerSec <= SL_SAMPLINGRATE_44_1) {
40260 return SL_SAMPLINGRATE_44_1;
40261 }
40262 if (samplesPerSec <= SL_SAMPLINGRATE_48) {
40263 return SL_SAMPLINGRATE_48;
40264 }
40265
40266 /* Android doesn't support more than 48000. */
40267#ifndef MA_ANDROID
40268 if (samplesPerSec <= SL_SAMPLINGRATE_64) {
40269 return SL_SAMPLINGRATE_64;
40270 }
40271 if (samplesPerSec <= SL_SAMPLINGRATE_88_2) {
40272 return SL_SAMPLINGRATE_88_2;
40273 }
40274 if (samplesPerSec <= SL_SAMPLINGRATE_96) {
40275 return SL_SAMPLINGRATE_96;
40276 }
40277 if (samplesPerSec <= SL_SAMPLINGRATE_192) {
40278 return SL_SAMPLINGRATE_192;
40279 }
40280#endif
40281
40282 return SL_SAMPLINGRATE_16;
40283}
40284
40285
40286static SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType)
40287{
40288 switch (streamType) {
40289 case ma_opensl_stream_type_voice: return SL_ANDROID_STREAM_VOICE;
40290 case ma_opensl_stream_type_system: return SL_ANDROID_STREAM_SYSTEM;
40291 case ma_opensl_stream_type_ring: return SL_ANDROID_STREAM_RING;
40292 case ma_opensl_stream_type_media: return SL_ANDROID_STREAM_MEDIA;
40293 case ma_opensl_stream_type_alarm: return SL_ANDROID_STREAM_ALARM;
40294 case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION;
40295 default: break;
40296 }
40297
40298 return SL_ANDROID_STREAM_VOICE;
40299}
40300
40301static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset)
40302{
40303 switch (recordingPreset) {
40304 case ma_opensl_recording_preset_generic: return SL_ANDROID_RECORDING_PRESET_GENERIC;
40305 case ma_opensl_recording_preset_camcorder: return SL_ANDROID_RECORDING_PRESET_CAMCORDER;
40306 case ma_opensl_recording_preset_voice_recognition: return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
40307 case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION;
40308 case ma_opensl_recording_preset_voice_unprocessed: return SL_ANDROID_RECORDING_PRESET_UNPROCESSED;
40309 default: break;
40310 }
40311
40312 return SL_ANDROID_RECORDING_PRESET_NONE;
40313}
40314
40315
40316static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
40317{
40318 ma_bool32 cbResult;
40319
40320 MA_ASSERT(pContext != NULL);
40321 MA_ASSERT(callback != NULL);
40322
40323 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. */
40324 if (g_maOpenSLInitCounter == 0) {
40325 return MA_INVALID_OPERATION;
40326 }
40327
40328 /*
40329 TODO: Test Me.
40330
40331 This is currently untested, so for now we are just returning default devices.
40332 */
40333#if 0 && !defined(MA_ANDROID)
40334 ma_bool32 isTerminated = MA_FALSE;
40335
40336 SLuint32 pDeviceIDs[128];
40337 SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]);
40338
40339 SLAudioIODeviceCapabilitiesItf deviceCaps;
40340 SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
40341 if (resultSL != SL_RESULT_SUCCESS) {
40342 /* The interface may not be supported so just report a default device. */
40343 goto return_default_device;
40344 }
40345
40346 /* Playback */
40347 if (!isTerminated) {
40348 resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs);
40349 if (resultSL != SL_RESULT_SUCCESS) {
40350 return ma_result_from_OpenSL(resultSL);
40351 }
40352
40353 for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
40354 ma_device_info deviceInfo;
40355 MA_ZERO_OBJECT(&deviceInfo);
40356 deviceInfo.id.opensl = pDeviceIDs[iDevice];
40357
40358 SLAudioOutputDescriptor desc;
40359 resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
40360 if (resultSL == SL_RESULT_SUCCESS) {
40361 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1);
40362
40363 ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
40364 if (cbResult == MA_FALSE) {
40365 isTerminated = MA_TRUE;
40366 break;
40367 }
40368 }
40369 }
40370 }
40371
40372 /* Capture */
40373 if (!isTerminated) {
40374 resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs);
40375 if (resultSL != SL_RESULT_SUCCESS) {
40376 return ma_result_from_OpenSL(resultSL);
40377 }
40378
40379 for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
40380 ma_device_info deviceInfo;
40381 MA_ZERO_OBJECT(&deviceInfo);
40382 deviceInfo.id.opensl = pDeviceIDs[iDevice];
40383
40384 SLAudioInputDescriptor desc;
40385 resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
40386 if (resultSL == SL_RESULT_SUCCESS) {
40387 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1);
40388
40389 ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
40390 if (cbResult == MA_FALSE) {
40391 isTerminated = MA_TRUE;
40392 break;
40393 }
40394 }
40395 }
40396 }
40397
40398 return MA_SUCCESS;
40399#else
40400 goto return_default_device;
40401#endif
40402
40403return_default_device:;
40404 cbResult = MA_TRUE;
40405
40406 /* Playback. */
40407 if (cbResult) {
40408 ma_device_info deviceInfo;
40409 MA_ZERO_OBJECT(&deviceInfo);
40410 deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT;
40411 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
40412 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
40413 }
40414
40415 /* Capture. */
40416 if (cbResult) {
40417 ma_device_info deviceInfo;
40418 MA_ZERO_OBJECT(&deviceInfo);
40419 deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT;
40420 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
40421 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
40422 }
40423
40424 return MA_SUCCESS;
40425}
40426
40427static void ma_context_add_data_format_ex__opensl(ma_context* pContext, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_device_info* pDeviceInfo)
40428{
40429 MA_ASSERT(pContext != NULL);
40430 MA_ASSERT(pDeviceInfo != NULL);
40431
40432 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
40433 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
40434 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
40435 pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
40436 pDeviceInfo->nativeDataFormatCount += 1;
40437}
40438
40439static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo)
40440{
40441 ma_uint32 minChannels = 1;
40442 ma_uint32 maxChannels = 2;
40445 ma_uint32 iChannel;
40446 ma_uint32 iSampleRate;
40447
40448 MA_ASSERT(pContext != NULL);
40449 MA_ASSERT(pDeviceInfo != NULL);
40450
40451 /*
40452 Each sample format can support mono and stereo, and we'll support a small subset of standard
40453 rates (up to 48000). A better solution would be to somehow find a native sample rate.
40454 */
40455 for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) {
40456 for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {
40457 ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];
40458 if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {
40459 ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo);
40460 }
40461 }
40462 }
40463}
40464
40465static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
40466{
40467 MA_ASSERT(pContext != NULL);
40468
40469 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. */
40470 if (g_maOpenSLInitCounter == 0) {
40471 return MA_INVALID_OPERATION;
40472 }
40473
40474 /*
40475 TODO: Test Me.
40476
40477 This is currently untested, so for now we are just returning default devices.
40478 */
40479#if 0 && !defined(MA_ANDROID)
40480 SLAudioIODeviceCapabilitiesItf deviceCaps;
40481 SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
40482 if (resultSL != SL_RESULT_SUCCESS) {
40483 /* The interface may not be supported so just report a default device. */
40484 goto return_default_device;
40485 }
40486
40487 if (deviceType == ma_device_type_playback) {
40488 SLAudioOutputDescriptor desc;
40489 resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
40490 if (resultSL != SL_RESULT_SUCCESS) {
40491 return ma_result_from_OpenSL(resultSL);
40492 }
40493
40494 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1);
40495 } else {
40496 SLAudioInputDescriptor desc;
40497 resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
40498 if (resultSL != SL_RESULT_SUCCESS) {
40499 return ma_result_from_OpenSL(resultSL);
40500 }
40501
40502 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1);
40503 }
40504
40505 goto return_detailed_info;
40506#else
40507 goto return_default_device;
40508#endif
40509
40510return_default_device:
40511 if (pDeviceID != NULL) {
40512 if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) ||
40513 (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) {
40514 return MA_NO_DEVICE; /* Don't know the device. */
40515 }
40516 }
40517
40518 /* ID and Name / Description */
40519 if (deviceType == ma_device_type_playback) {
40520 pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT;
40521 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
40522 } else {
40523 pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT;
40524 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
40525 }
40526
40527 pDeviceInfo->isDefault = MA_TRUE;
40528
40529 goto return_detailed_info;
40530
40531
40532return_detailed_info:
40533
40534 /*
40535 For now we're just outputting a set of values that are supported by the API but not necessarily supported
40536 by the device natively. Later on we should work on this so that it more closely reflects the device's
40537 actual native format.
40538 */
40539 pDeviceInfo->nativeDataFormatCount = 0;
40540#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
40541 ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo);
40542#endif
40543 ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo);
40544 ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo);
40545
40546 return MA_SUCCESS;
40547}
40548
40549
40550#ifdef MA_ANDROID
40551/*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/
40552static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
40553{
40554 ma_device* pDevice = (ma_device*)pUserData;
40555 size_t periodSizeInBytes;
40556 ma_uint8* pBuffer;
40557 SLresult resultSL;
40558
40559 MA_ASSERT(pDevice != NULL);
40560
40561 (void)pBufferQueue;
40562
40563 /*
40564 For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like
40565 OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this,
40566 but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :(
40567 */
40568
40569 /* Don't do anything if the device is not started. */
40571 return;
40572 }
40573
40574 /* Don't do anything if the device is being drained. */
40575 if (pDevice->opensl.isDrainingCapture) {
40576 return;
40577 }
40578
40580 pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes);
40581
40583
40584 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes);
40585 if (resultSL != SL_RESULT_SUCCESS) {
40586 return;
40587 }
40588
40589 pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods;
40590}
40591
40592static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
40593{
40594 ma_device* pDevice = (ma_device*)pUserData;
40595 size_t periodSizeInBytes;
40596 ma_uint8* pBuffer;
40597 SLresult resultSL;
40598
40599 MA_ASSERT(pDevice != NULL);
40600
40601 (void)pBufferQueue;
40602
40603 /* Don't do anything if the device is not started. */
40605 return;
40606 }
40607
40608 /* Don't do anything if the device is being drained. */
40609 if (pDevice->opensl.isDrainingPlayback) {
40610 return;
40611 }
40612
40614 pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes);
40615
40617
40618 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes);
40619 if (resultSL != SL_RESULT_SUCCESS) {
40620 return;
40621 }
40622
40623 pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods;
40624}
40625#endif
40626
40627static ma_result ma_device_uninit__opensl(ma_device* pDevice)
40628{
40629 MA_ASSERT(pDevice != NULL);
40630
40631 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. */
40632 if (g_maOpenSLInitCounter == 0) {
40633 return MA_INVALID_OPERATION;
40634 }
40635
40636 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
40637 if (pDevice->opensl.pAudioRecorderObj) {
40638 MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj);
40639 }
40640
40641 ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks);
40642 }
40643
40644 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
40645 if (pDevice->opensl.pAudioPlayerObj) {
40646 MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj);
40647 }
40648 if (pDevice->opensl.pOutputMixObj) {
40649 MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj);
40650 }
40651
40652 ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks);
40653 }
40654
40655 return MA_SUCCESS;
40656}
40657
40658#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
40659typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM;
40660#else
40661typedef SLDataFormat_PCM ma_SLDataFormat_PCM;
40662#endif
40663
40664static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat)
40665{
40666 /* We need to convert our format/channels/rate so that they aren't set to default. */
40667 if (format == ma_format_unknown) {
40668 format = MA_DEFAULT_FORMAT;
40669 }
40670 if (channels == 0) {
40671 channels = MA_DEFAULT_CHANNELS;
40672 }
40673 if (sampleRate == 0) {
40674 sampleRate = MA_DEFAULT_SAMPLE_RATE;
40675 }
40676
40677#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
40678 if (format == ma_format_f32) {
40679 pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
40680 pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
40681 } else {
40682 pDataFormat->formatType = SL_DATAFORMAT_PCM;
40683 }
40684#else
40685 pDataFormat->formatType = SL_DATAFORMAT_PCM;
40686#endif
40687
40688 pDataFormat->numChannels = channels;
40689 ((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 */
40690 pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8;
40691 pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels);
40692 pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
40693
40694 /*
40695 Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html
40696 - Only mono and stereo is supported.
40697 - Only u8 and s16 formats are supported.
40698 - Maximum sample rate of 48000.
40699 */
40700#ifdef MA_ANDROID
40701 if (pDataFormat->numChannels > 2) {
40702 pDataFormat->numChannels = 2;
40703 }
40704#if __ANDROID_API__ >= 21
40705 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
40706 /* It's floating point. */
40707 MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
40708 if (pDataFormat->bitsPerSample > 32) {
40709 pDataFormat->bitsPerSample = 32;
40710 }
40711 } else {
40712 if (pDataFormat->bitsPerSample > 16) {
40713 pDataFormat->bitsPerSample = 16;
40714 }
40715 }
40716#else
40717 if (pDataFormat->bitsPerSample > 16) {
40718 pDataFormat->bitsPerSample = 16;
40719 }
40720#endif
40721 if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) {
40722 ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48;
40723 }
40724#endif
40725
40726 pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */
40727
40728 return MA_SUCCESS;
40729}
40730
40731static 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)
40732{
40733 ma_bool32 isFloatingPoint = MA_FALSE;
40734#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
40735 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
40736 MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
40737 isFloatingPoint = MA_TRUE;
40738 }
40739#endif
40740 if (isFloatingPoint) {
40741 if (pDataFormat->bitsPerSample == 32) {
40742 *pFormat = ma_format_f32;
40743 }
40744 } else {
40745 if (pDataFormat->bitsPerSample == 8) {
40746 *pFormat = ma_format_u8;
40747 } else if (pDataFormat->bitsPerSample == 16) {
40748 *pFormat = ma_format_s16;
40749 } else if (pDataFormat->bitsPerSample == 24) {
40750 *pFormat = ma_format_s24;
40751 } else if (pDataFormat->bitsPerSample == 32) {
40752 *pFormat = ma_format_s32;
40753 }
40754 }
40755
40756 *pChannels = pDataFormat->numChannels;
40757 *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000;
40758 ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap);
40759
40760 return MA_SUCCESS;
40761}
40762
40763static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
40764{
40765#ifdef MA_ANDROID
40766 SLDataLocator_AndroidSimpleBufferQueue queue;
40767 SLresult resultSL;
40768 size_t bufferSizeInBytes;
40769 SLInterfaceID itfIDs[2];
40770 const SLboolean itfIDsRequired[] = {
40771 SL_BOOLEAN_TRUE, /* SL_IID_ANDROIDSIMPLEBUFFERQUEUE */
40772 SL_BOOLEAN_FALSE /* SL_IID_ANDROIDCONFIGURATION */
40773 };
40774#endif
40775
40776 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. */
40777 if (g_maOpenSLInitCounter == 0) {
40778 return MA_INVALID_OPERATION;
40779 }
40780
40781 if (pConfig->deviceType == ma_device_type_loopback) {
40783 }
40784
40785 /*
40786 For now, only supporting Android implementations of OpenSL|ES since that's the only one I've
40787 been able to test with and I currently depend on Android-specific extensions (simple buffer
40788 queues).
40789 */
40790#ifdef MA_ANDROID
40791 itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
40792 itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION;
40793
40794 /* No exclusive mode with OpenSL|ES. */
40795 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
40796 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
40798 }
40799
40800 /* Now we can start initializing the device properly. */
40801 MA_ASSERT(pDevice != NULL);
40802 MA_ZERO_OBJECT(&pDevice->opensl);
40803
40804 queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
40805
40806 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
40807 ma_SLDataFormat_PCM pcm;
40808 SLDataLocator_IODevice locatorDevice;
40809 SLDataSource source;
40810 SLDataSink sink;
40811 SLAndroidConfigurationItf pRecorderConfig;
40812
40813 ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm);
40814
40815 locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE;
40816 locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT;
40817 locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; /* Must always use the default device with Android. */
40818 locatorDevice.device = NULL;
40819
40820 source.pLocator = &locatorDevice;
40821 source.pFormat = NULL;
40822
40823 queue.numBuffers = pDescriptorCapture->periodCount;
40824
40825 sink.pLocator = &queue;
40826 sink.pFormat = (SLDataFormat_PCM*)&pcm;
40827
40828 resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
40829 if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) {
40830 /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
40831 pcm.formatType = SL_DATAFORMAT_PCM;
40832 pcm.numChannels = 1;
40833 ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */
40834 pcm.bitsPerSample = 16;
40835 pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
40836 pcm.channelMask = 0;
40837 resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
40838 }
40839
40840 if (resultSL != SL_RESULT_SUCCESS) {
40841 ma_device_uninit__opensl(pDevice);
40842 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder.");
40843 return ma_result_from_OpenSL(resultSL);
40844 }
40845
40846
40847 /* Set the recording preset before realizing the player. */
40849 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig);
40850 if (resultSL == SL_RESULT_SUCCESS) {
40851 SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset);
40852 resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32));
40853 if (resultSL != SL_RESULT_SUCCESS) {
40854 /* Failed to set the configuration. Just keep going. */
40855 }
40856 }
40857 }
40858
40859 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE);
40860 if (resultSL != SL_RESULT_SUCCESS) {
40861 ma_device_uninit__opensl(pDevice);
40862 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.");
40863 return ma_result_from_OpenSL(resultSL);
40864 }
40865
40866 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder);
40867 if (resultSL != SL_RESULT_SUCCESS) {
40868 ma_device_uninit__opensl(pDevice);
40869 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.");
40870 return ma_result_from_OpenSL(resultSL);
40871 }
40872
40873 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture);
40874 if (resultSL != SL_RESULT_SUCCESS) {
40875 ma_device_uninit__opensl(pDevice);
40876 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.");
40877 return ma_result_from_OpenSL(resultSL);
40878 }
40879
40880 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice);
40881 if (resultSL != SL_RESULT_SUCCESS) {
40882 ma_device_uninit__opensl(pDevice);
40883 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.");
40884 return ma_result_from_OpenSL(resultSL);
40885 }
40886
40887 /* The internal format is determined by the "pcm" object. */
40888 ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap));
40889
40890 /* Buffer. */
40891 pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
40892 pDevice->opensl.currentBufferIndexCapture = 0;
40893
40894 bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount;
40895 pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);
40896 if (pDevice->opensl.pBufferCapture == NULL) {
40897 ma_device_uninit__opensl(pDevice);
40898 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.");
40899 return MA_OUT_OF_MEMORY;
40900 }
40901 MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes);
40902 }
40903
40904 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
40905 ma_SLDataFormat_PCM pcm;
40906 SLDataSource source;
40907 SLDataLocator_OutputMix outmixLocator;
40908 SLDataSink sink;
40909 SLAndroidConfigurationItf pPlayerConfig;
40910
40911 ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm);
40912
40913 resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL);
40914 if (resultSL != SL_RESULT_SUCCESS) {
40915 ma_device_uninit__opensl(pDevice);
40916 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix.");
40917 return ma_result_from_OpenSL(resultSL);
40918 }
40919
40920 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE);
40921 if (resultSL != SL_RESULT_SUCCESS) {
40922 ma_device_uninit__opensl(pDevice);
40923 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.");
40924 return ma_result_from_OpenSL(resultSL);
40925 }
40926
40927 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix);
40928 if (resultSL != SL_RESULT_SUCCESS) {
40929 ma_device_uninit__opensl(pDevice);
40930 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.");
40931 return ma_result_from_OpenSL(resultSL);
40932 }
40933
40934 /* Set the output device. */
40935 if (pDescriptorPlayback->pDeviceID != NULL) {
40936 SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl;
40937 MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL);
40938 }
40939
40940 queue.numBuffers = pDescriptorPlayback->periodCount;
40941
40942 source.pLocator = &queue;
40943 source.pFormat = (SLDataFormat_PCM*)&pcm;
40944
40945 outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
40946 outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj;
40947
40948 sink.pLocator = &outmixLocator;
40949 sink.pFormat = NULL;
40950
40951 resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
40952 if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) {
40953 /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
40954 pcm.formatType = SL_DATAFORMAT_PCM;
40955 pcm.numChannels = 2;
40956 ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;
40957 pcm.bitsPerSample = 16;
40958 pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
40959 pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
40960 resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
40961 }
40962
40963 if (resultSL != SL_RESULT_SUCCESS) {
40964 ma_device_uninit__opensl(pDevice);
40965 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player.");
40966 return ma_result_from_OpenSL(resultSL);
40967 }
40968
40969
40970 /* Set the stream type before realizing the player. */
40972 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig);
40973 if (resultSL == SL_RESULT_SUCCESS) {
40974 SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType);
40975 resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
40976 if (resultSL != SL_RESULT_SUCCESS) {
40977 /* Failed to set the configuration. Just keep going. */
40978 }
40979 }
40980 }
40981
40982 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE);
40983 if (resultSL != SL_RESULT_SUCCESS) {
40984 ma_device_uninit__opensl(pDevice);
40985 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.");
40986 return ma_result_from_OpenSL(resultSL);
40987 }
40988
40989 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer);
40990 if (resultSL != SL_RESULT_SUCCESS) {
40991 ma_device_uninit__opensl(pDevice);
40992 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.");
40993 return ma_result_from_OpenSL(resultSL);
40994 }
40995
40996 resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback);
40997 if (resultSL != SL_RESULT_SUCCESS) {
40998 ma_device_uninit__opensl(pDevice);
40999 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.");
41000 return ma_result_from_OpenSL(resultSL);
41001 }
41002
41003 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice);
41004 if (resultSL != SL_RESULT_SUCCESS) {
41005 ma_device_uninit__opensl(pDevice);
41006 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.");
41007 return ma_result_from_OpenSL(resultSL);
41008 }
41009
41010 /* The internal format is determined by the "pcm" object. */
41011 ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap));
41012
41013 /* Buffer. */
41014 pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
41015 pDevice->opensl.currentBufferIndexPlayback = 0;
41016
41017 bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount;
41018 pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);
41019 if (pDevice->opensl.pBufferPlayback == NULL) {
41020 ma_device_uninit__opensl(pDevice);
41021 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.");
41022 return MA_OUT_OF_MEMORY;
41023 }
41024 MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes);
41025 }
41026
41027 return MA_SUCCESS;
41028#else
41029 return MA_NO_BACKEND; /* Non-Android implementations are not supported. */
41030#endif
41031}
41032
41033static ma_result ma_device_start__opensl(ma_device* pDevice)
41034{
41035 SLresult resultSL;
41036 size_t periodSizeInBytes;
41037 ma_uint32 iPeriod;
41038
41039 MA_ASSERT(pDevice != NULL);
41040
41041 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. */
41042 if (g_maOpenSLInitCounter == 0) {
41043 return MA_INVALID_OPERATION;
41044 }
41045
41046 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
41047 resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING);
41048 if (resultSL != SL_RESULT_SUCCESS) {
41049 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device.");
41050 return ma_result_from_OpenSL(resultSL);
41051 }
41052
41054 for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
41055 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes);
41056 if (resultSL != SL_RESULT_SUCCESS) {
41057 MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
41058 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device.");
41059 return ma_result_from_OpenSL(resultSL);
41060 }
41061 }
41062 }
41063
41064 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
41065 resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING);
41066 if (resultSL != SL_RESULT_SUCCESS) {
41067 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device.");
41068 return ma_result_from_OpenSL(resultSL);
41069 }
41070
41071 /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueue silent buffers. */
41072 if (pDevice->type == ma_device_type_duplex) {
41073 MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
41074 } else {
41075 ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback);
41076 }
41077
41079 for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
41080 resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes);
41081 if (resultSL != SL_RESULT_SUCCESS) {
41082 MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
41083 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device.");
41084 return ma_result_from_OpenSL(resultSL);
41085 }
41086 }
41087 }
41088
41089 return MA_SUCCESS;
41090}
41091
41092static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType)
41093{
41094 SLAndroidSimpleBufferQueueItf pBufferQueue;
41095
41096 MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback);
41097
41098 if (pDevice->type == ma_device_type_capture) {
41099 pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture;
41100 pDevice->opensl.isDrainingCapture = MA_TRUE;
41101 } else {
41102 pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback;
41103 pDevice->opensl.isDrainingPlayback = MA_TRUE;
41104 }
41105
41106 for (;;) {
41107 SLAndroidSimpleBufferQueueState state;
41108
41109 MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state);
41110 if (state.count == 0) {
41111 break;
41112 }
41113
41114 ma_sleep(10);
41115 }
41116
41117 if (pDevice->type == ma_device_type_capture) {
41118 pDevice->opensl.isDrainingCapture = MA_FALSE;
41119 } else {
41120 pDevice->opensl.isDrainingPlayback = MA_FALSE;
41121 }
41122
41123 return MA_SUCCESS;
41124}
41125
41126static ma_result ma_device_stop__opensl(ma_device* pDevice)
41127{
41128 SLresult resultSL;
41129
41130 MA_ASSERT(pDevice != NULL);
41131
41132 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. */
41133 if (g_maOpenSLInitCounter == 0) {
41134 return MA_INVALID_OPERATION;
41135 }
41136
41137 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
41138 ma_device_drain__opensl(pDevice, ma_device_type_capture);
41139
41140 resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
41141 if (resultSL != SL_RESULT_SUCCESS) {
41142 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device.");
41143 return ma_result_from_OpenSL(resultSL);
41144 }
41145
41146 MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture);
41147 }
41148
41149 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
41150 ma_device_drain__opensl(pDevice, ma_device_type_playback);
41151
41152 resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
41153 if (resultSL != SL_RESULT_SUCCESS) {
41154 ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device.");
41155 return ma_result_from_OpenSL(resultSL);
41156 }
41157
41158 MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback);
41159 }
41160
41161 /* 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. */
41162 ma_device__on_notification_stopped(pDevice);
41163
41164 return MA_SUCCESS;
41165}
41166
41167
41168static ma_result ma_context_uninit__opensl(ma_context* pContext)
41169{
41170 MA_ASSERT(pContext != NULL);
41171 MA_ASSERT(pContext->backend == ma_backend_opensl);
41172 (void)pContext;
41173
41174 /* Uninit global data. */
41175 ma_spinlock_lock(&g_maOpenSLSpinlock);
41176 {
41177 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(). */
41178
41179 g_maOpenSLInitCounter -= 1;
41180 if (g_maOpenSLInitCounter == 0) {
41181 (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
41182 }
41183 }
41184 ma_spinlock_unlock(&g_maOpenSLSpinlock);
41185
41186 return MA_SUCCESS;
41187}
41188
41189static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle)
41190{
41191 /* 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. */
41192 ma_handle* p = (ma_handle*)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, pName);
41193 if (p == NULL) {
41194 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName);
41195 return MA_NO_BACKEND;
41196 }
41197
41198 *pHandle = *p;
41199 return MA_SUCCESS;
41200}
41201
41202static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext)
41203{
41204 g_maOpenSLInitCounter += 1;
41205 if (g_maOpenSLInitCounter == 1) {
41206 SLresult resultSL;
41207
41208 resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL);
41209 if (resultSL != SL_RESULT_SUCCESS) {
41210 g_maOpenSLInitCounter -= 1;
41211 return ma_result_from_OpenSL(resultSL);
41212 }
41213
41214 (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE);
41215
41216 resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL);
41217 if (resultSL != SL_RESULT_SUCCESS) {
41218 (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
41219 g_maOpenSLInitCounter -= 1;
41220 return ma_result_from_OpenSL(resultSL);
41221 }
41222 }
41223
41224 return MA_SUCCESS;
41225}
41226
41227static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
41228{
41229 ma_result result;
41230
41231#if !defined(MA_NO_RUNTIME_LINKING)
41232 size_t i;
41233 const char* libOpenSLESNames[] = {
41234 "libOpenSLES.so"
41235 };
41236#endif
41237
41238 MA_ASSERT(pContext != NULL);
41239
41240 (void)pConfig;
41241
41242#if !defined(MA_NO_RUNTIME_LINKING)
41243 /*
41244 Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One
41245 report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime
41246 and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any
41247 references to the symbols and will hopefully skip the checks.
41248 */
41249 for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) {
41250 pContext->opensl.libOpenSLES = ma_dlopen(ma_context_get_log(pContext), libOpenSLESNames[i]);
41251 if (pContext->opensl.libOpenSLES != NULL) {
41252 break;
41253 }
41254 }
41255
41256 if (pContext->opensl.libOpenSLES == NULL) {
41257 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so");
41258 return MA_NO_BACKEND;
41259 }
41260
41261 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE);
41262 if (result != MA_SUCCESS) {
41263 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
41264 return result;
41265 }
41266
41267 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES);
41268 if (result != MA_SUCCESS) {
41269 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
41270 return result;
41271 }
41272
41273 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE);
41274 if (result != MA_SUCCESS) {
41275 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
41276 return result;
41277 }
41278
41279 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD);
41280 if (result != MA_SUCCESS) {
41281 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
41282 return result;
41283 }
41284
41285 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY);
41286 if (result != MA_SUCCESS) {
41287 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
41288 return result;
41289 }
41290
41291 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX);
41292 if (result != MA_SUCCESS) {
41293 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
41294 return result;
41295 }
41296
41297 result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION);
41298 if (result != MA_SUCCESS) {
41299 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
41300 return result;
41301 }
41302
41303 pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, "slCreateEngine");
41304 if (pContext->opensl.slCreateEngine == NULL) {
41305 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
41306 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine.");
41307 return MA_NO_BACKEND;
41308 }
41309#else
41310 pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE;
41311 pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES;
41312 pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
41313 pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD;
41314 pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY;
41315 pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX;
41316 pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION;
41317 pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine;
41318#endif
41319
41320
41321 /* Initialize global data first if applicable. */
41322 ma_spinlock_lock(&g_maOpenSLSpinlock);
41323 {
41324 result = ma_context_init_engine_nolock__opensl(pContext);
41325 }
41326 ma_spinlock_unlock(&g_maOpenSLSpinlock);
41327
41328 if (result != MA_SUCCESS) {
41329 ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
41330 ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine.");
41331 return result;
41332 }
41333
41334 pCallbacks->onContextInit = ma_context_init__opensl;
41335 pCallbacks->onContextUninit = ma_context_uninit__opensl;
41336 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl;
41337 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl;
41338 pCallbacks->onDeviceInit = ma_device_init__opensl;
41339 pCallbacks->onDeviceUninit = ma_device_uninit__opensl;
41340 pCallbacks->onDeviceStart = ma_device_start__opensl;
41341 pCallbacks->onDeviceStop = ma_device_stop__opensl;
41342 pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */
41343 pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */
41344 pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */
41345
41346 return MA_SUCCESS;
41347}
41348#endif /* OpenSL|ES */
41349
41350
41351/******************************************************************************
41352
41353Web Audio Backend
41354
41355******************************************************************************/
41356#ifdef MA_HAS_WEBAUDIO
41357#include <emscripten/emscripten.h>
41358
41359#if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32)))
41360 #include <emscripten/webaudio.h>
41361 #define MA_SUPPORT_AUDIO_WORKLETS
41362
41363 #if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 70)))
41364 #define MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE
41365 #endif
41366#endif
41367
41368/*
41369TODO: Version 0.12: Swap this logic around so that AudioWorklets are used by default. Add MA_NO_AUDIO_WORKLETS.
41370*/
41371#if defined(MA_ENABLE_AUDIO_WORKLETS) && defined(MA_SUPPORT_AUDIO_WORKLETS)
41372 #define MA_USE_AUDIO_WORKLETS
41373#endif
41374
41375/* The thread stack size must be a multiple of 16. */
41376#ifndef MA_AUDIO_WORKLETS_THREAD_STACK_SIZE
41377#define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 131072
41378#endif
41379
41380#if defined(MA_USE_AUDIO_WORKLETS)
41381#define MA_WEBAUDIO_LATENCY_HINT_BALANCED "balanced"
41382#define MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE "interactive"
41383#define MA_WEBAUDIO_LATENCY_HINT_PLAYBACK "playback"
41384#endif
41385
41386static ma_bool32 ma_is_capture_supported__webaudio()
41387{
41388 return EM_ASM_INT({
41389 return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined);
41390 }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */
41391}
41392
41393#ifdef __cplusplus
41394extern "C" {
41395#endif
41396void* EMSCRIPTEN_KEEPALIVE ma_malloc_emscripten(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
41397{
41398 return ma_malloc(sz, pAllocationCallbacks);
41399}
41400
41401void EMSCRIPTEN_KEEPALIVE ma_free_emscripten(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
41402{
41403 ma_free(p, pAllocationCallbacks);
41404}
41405
41406void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
41407{
41408 ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount);
41409}
41410
41411void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
41412{
41413 ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount);
41414}
41415#ifdef __cplusplus
41416}
41417#endif
41418
41419static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
41420{
41421 ma_bool32 cbResult = MA_TRUE;
41422
41423 MA_ASSERT(pContext != NULL);
41424 MA_ASSERT(callback != NULL);
41425
41426 /* Only supporting default devices for now. */
41427
41428 /* Playback. */
41429 if (cbResult) {
41430 ma_device_info deviceInfo;
41431 MA_ZERO_OBJECT(&deviceInfo);
41432 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
41433 deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */
41434 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
41435 }
41436
41437 /* Capture. */
41438 if (cbResult) {
41439 if (ma_is_capture_supported__webaudio()) {
41440 ma_device_info deviceInfo;
41441 MA_ZERO_OBJECT(&deviceInfo);
41442 ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
41443 deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */
41444 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
41445 }
41446 }
41447
41448 return MA_SUCCESS;
41449}
41450
41451static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
41452{
41453 MA_ASSERT(pContext != NULL);
41454
41455 if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
41456 return MA_NO_DEVICE;
41457 }
41458
41459 MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio));
41460
41461 /* Only supporting default devices for now. */
41462 (void)pDeviceID;
41463 if (deviceType == ma_device_type_playback) {
41464 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
41465 } else {
41466 ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
41467 }
41468
41469 /* Only supporting default devices. */
41470 pDeviceInfo->isDefault = MA_TRUE;
41471
41472 /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */
41473 pDeviceInfo->nativeDataFormats[0].flags = 0;
41474 pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown;
41475 pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channels are supported. */
41476 pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({
41477 try {
41478 var temp = new (window.AudioContext || window.webkitAudioContext)();
41479 var sampleRate = temp.sampleRate;
41480 temp.close();
41481 return sampleRate;
41482 } catch(e) {
41483 return 0;
41484 }
41485 }, 0); /* Must pass in a dummy argument for C99 compatibility. */
41486
41487 if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) {
41488 return MA_NO_DEVICE;
41489 }
41490
41491 pDeviceInfo->nativeDataFormatCount = 1;
41492
41493 return MA_SUCCESS;
41494}
41495
41496static ma_result ma_device_uninit__webaudio(ma_device* pDevice)
41497{
41498 MA_ASSERT(pDevice != NULL);
41499
41500 #if defined(MA_USE_AUDIO_WORKLETS)
41501 {
41502 EM_ASM({
41503 var device = window.miniaudio.get_device_by_index($0);
41504
41505 if (device.streamNode !== undefined) {
41506 device.streamNode.disconnect();
41507 device.streamNode = undefined;
41508 }
41509
41510 device.pDevice = undefined;
41511 }, pDevice->webaudio.deviceIndex);
41512
41513 emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet);
41514 emscripten_destroy_audio_context(pDevice->webaudio.audioContext);
41515 ma_free(pDevice->webaudio.pStackBuffer, &pDevice->pContext->allocationCallbacks);
41516 }
41517 #else
41518 {
41519 EM_ASM({
41520 var device = window.miniaudio.get_device_by_index($0);
41521
41522 /* Make sure all nodes are disconnected and marked for collection. */
41523 if (device.scriptNode !== undefined) {
41524 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... */
41525 device.scriptNode.disconnect();
41526 device.scriptNode = undefined;
41527 }
41528
41529 if (device.streamNode !== undefined) {
41530 device.streamNode.disconnect();
41531 device.streamNode = undefined;
41532 }
41533
41534 /*
41535 Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want
41536 to clear the callback before closing.
41537 */
41538 device.webaudio.close();
41539 device.webaudio = undefined;
41540 device.pDevice = undefined;
41541 }, pDevice->webaudio.deviceIndex);
41542 }
41543 #endif
41544
41545 /* Clean up the device on the JS side. */
41546 EM_ASM({
41547 window.miniaudio.untrack_device_by_index($0);
41548 }, pDevice->webaudio.deviceIndex);
41549
41550 ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);
41551
41552 return MA_SUCCESS;
41553}
41554
41555#if !defined(MA_USE_AUDIO_WORKLETS)
41556static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
41557{
41558 /*
41559 There have been reports of the default buffer size being too small on some browsers. If we're using
41560 the default buffer size, we'll make sure the period size is bigger than our standard defaults.
41561 */
41562 ma_uint32 periodSizeInFrames;
41563
41564 if (nativeSampleRate == 0) {
41565 nativeSampleRate = MA_DEFAULT_SAMPLE_RATE;
41566 }
41567
41568 if (pDescriptor->periodSizeInFrames == 0) {
41569 if (pDescriptor->periodSizeInMilliseconds == 0) {
41570 if (performanceProfile == ma_performance_profile_low_latency) {
41571 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */
41572 } else {
41573 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate);
41574 }
41575 } else {
41576 periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
41577 }
41578 } else {
41579 periodSizeInFrames = pDescriptor->periodSizeInFrames;
41580 }
41581
41582 /* The size of the buffer must be a power of 2 and between 256 and 16384. */
41583 if (periodSizeInFrames < 256) {
41584 periodSizeInFrames = 256;
41585 } else if (periodSizeInFrames > 16384) {
41586 periodSizeInFrames = 16384;
41587 } else {
41588 periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames);
41589 }
41590
41591 return periodSizeInFrames;
41592}
41593#endif
41594
41595
41596#if defined(MA_USE_AUDIO_WORKLETS)
41597typedef struct
41598{
41599 ma_device* pDevice;
41600 const ma_device_config* pConfig;
41601 ma_device_descriptor* pDescriptorPlayback;
41602 ma_device_descriptor* pDescriptorCapture;
41603} ma_audio_worklet_thread_initialized_data;
41604
41605static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData)
41606{
41607 ma_device* pDevice = (ma_device*)pUserData;
41608 ma_uint32 frameCount;
41609
41610 (void)paramCount;
41611 (void)pParams;
41612
41613 /*
41614 The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels
41615 like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer
41616 to variables instead of a hard coded number. In any case, will follow along for the time being.
41617
41618 Unfortunately the audio data is not interleaved so we'll need to convert it before we give the data to miniaudio
41619 for further processing.
41620 */
41621 if (pDevice->type == ma_device_type_playback) {
41622 frameCount = pDevice->playback.internalPeriodSizeInFrames;
41623 } else {
41624 frameCount = pDevice->capture.internalPeriodSizeInFrames;
41625 }
41626
41628 /* Fill the output buffer with zero to avoid a noise sound */
41629 for (int i = 0; i < outputCount; i += 1) {
41630 MA_ZERO_MEMORY(pOutputs[i].data, pOutputs[i].numberOfChannels * frameCount * sizeof(float));
41631 }
41632
41633 return EM_TRUE;
41634 }
41635
41636 if (inputCount > 0) {
41637 /* Input data needs to be interleaved before we hand it to the client. */
41638 for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) {
41639 for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) {
41640 pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + iFrame];
41641 }
41642 }
41643
41644 ma_device_process_pcm_frames_capture__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer);
41645 }
41646
41647 if (outputCount > 0) {
41648 /* If it's a capture-only device, we'll need to output silence. */
41649 if (pDevice->type == ma_device_type_capture) {
41650 MA_ZERO_MEMORY(pOutputs[0].data, frameCount * pDevice->playback.internalChannels * sizeof(float));
41651 } else {
41652 ma_device_process_pcm_frames_playback__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer);
41653
41654 /* We've read the data from the client. Now we need to deinterleave the buffer and output to the output buffer. */
41655 for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) {
41656 for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) {
41657 pOutputs[0].data[frameCount*iChannel + iFrame] = pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->playback.internalChannels + iChannel];
41658 }
41659 }
41660 }
41661 }
41662
41663 return EM_TRUE;
41664}
41665
41666
41667static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData)
41668{
41669 ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData;
41670 EmscriptenAudioWorkletNodeCreateOptions audioWorkletOptions;
41671 int channels = 0;
41672 size_t intermediaryBufferSizeInFrames;
41673 int sampleRate;
41674
41675 if (success == EM_FALSE) {
41676 pParameters->pDevice->webaudio.initResult = MA_ERROR;
41677 ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);
41678 return;
41679 }
41680
41681 /* The next step is to initialize the audio worklet node. */
41682 MA_ZERO_OBJECT(&audioWorkletOptions);
41683
41684 /*
41685 The way channel counts work with Web Audio is confusing. As far as I can tell, there's no way to know the channel
41686 count from MediaStreamAudioSourceNode (what we use for capture)? The only way to have control is to configure an
41687 output channel count on the capture side. This is slightly confusing for capture mode because intuitively you
41688 wouldn't actually connect an output to an input-only node, but this is what we'll have to do in order to have
41689 proper control over the channel count. In the capture case, we'll have to output silence to its output node.
41690 */
41691 if (pParameters->pConfig->deviceType == ma_device_type_capture) {
41692 channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS);
41693 audioWorkletOptions.numberOfInputs = 1;
41694 } else {
41695 channels = (int)((pParameters->pDescriptorPlayback->channels > 0) ? pParameters->pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS);
41696
41697 if (pParameters->pConfig->deviceType == ma_device_type_duplex) {
41698 audioWorkletOptions.numberOfInputs = 1;
41699 } else {
41700 audioWorkletOptions.numberOfInputs = 0;
41701 }
41702 }
41703
41704 audioWorkletOptions.numberOfOutputs = 1;
41705 audioWorkletOptions.outputChannelCounts = &channels;
41706
41707
41708 /*
41709 Now that we know the channel count to use we can allocate the intermediary buffer. The
41710 intermediary buffer is used for interleaving and deinterleaving.
41711 */
41712 #if defined(MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE)
41713 {
41714 intermediaryBufferSizeInFrames = (size_t)emscripten_audio_context_quantum_size(audioContext);
41715 }
41716 #else
41717 {
41718 intermediaryBufferSizeInFrames = 128;
41719 }
41720 #endif
41721
41722 pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks);
41723 if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) {
41724 pParameters->pDevice->webaudio.initResult = MA_OUT_OF_MEMORY;
41725 ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);
41726 return;
41727 }
41728
41729 pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice);
41730
41731 /* With the audio worklet initialized we can now attach it to the graph. */
41732 if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) {
41733 ma_result attachmentResult = (ma_result)EM_ASM_INT({
41734 var getUserMediaResult = 0;
41735 var audioWorklet = emscriptenGetAudioObject($0);
41736 var audioContext = emscriptenGetAudioObject($1);
41737
41738 navigator.mediaDevices.getUserMedia({audio:true, video:false})
41739 .then(function(stream) {
41740 audioContext.streamNode = audioContext.createMediaStreamSource(stream);
41741 audioContext.streamNode.connect(audioWorklet);
41742 audioWorklet.connect(audioContext.destination);
41743 getUserMediaResult = 0; /* 0 = MA_SUCCESS */
41744 })
41745 .catch(function(error) {
41746 console.log("navigator.mediaDevices.getUserMedia Failed: " + error);
41747 getUserMediaResult = -1; /* -1 = MA_ERROR */
41748 });
41749
41750 return getUserMediaResult;
41751 }, pParameters->pDevice->webaudio.audioWorklet, audioContext);
41752
41753 if (attachmentResult != MA_SUCCESS) {
41754 ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect capture node.");
41755 emscripten_destroy_web_audio_node(pParameters->pDevice->webaudio.audioWorklet);
41756 pParameters->pDevice->webaudio.initResult = attachmentResult;
41757 ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);
41758 return;
41759 }
41760 }
41761
41762 /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */
41763 if (pParameters->pConfig->deviceType == ma_device_type_playback) {
41764 ma_result attachmentResult = (ma_result)EM_ASM_INT({
41765 var audioWorklet = emscriptenGetAudioObject($0);
41766 var audioContext = emscriptenGetAudioObject($1);
41767 audioWorklet.connect(audioContext.destination);
41768 return 0; /* 0 = MA_SUCCESS */
41769 }, pParameters->pDevice->webaudio.audioWorklet, audioContext);
41770
41771 if (attachmentResult != MA_SUCCESS) {
41772 ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect playback node.");
41773 pParameters->pDevice->webaudio.initResult = attachmentResult;
41774 ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);
41775 return;
41776 }
41777 }
41778
41779 /* We need to update the descriptors so that they reflect the internal data format. Both capture and playback should be the same. */
41780 sampleRate = EM_ASM_INT({ return emscriptenGetAudioObject($0).sampleRate; }, audioContext);
41781
41782 if (pParameters->pDescriptorCapture != NULL) {
41783 pParameters->pDescriptorCapture->format = ma_format_f32;
41784 pParameters->pDescriptorCapture->channels = (ma_uint32)channels;
41785 pParameters->pDescriptorCapture->sampleRate = (ma_uint32)sampleRate;
41786 ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorCapture->channelMap, ma_countof(pParameters->pDescriptorCapture->channelMap), pParameters->pDescriptorCapture->channels);
41787 pParameters->pDescriptorCapture->periodSizeInFrames = intermediaryBufferSizeInFrames;
41788 pParameters->pDescriptorCapture->periodCount = 1;
41789 }
41790
41791 if (pParameters->pDescriptorPlayback != NULL) {
41792 pParameters->pDescriptorPlayback->format = ma_format_f32;
41793 pParameters->pDescriptorPlayback->channels = (ma_uint32)channels;
41794 pParameters->pDescriptorPlayback->sampleRate = (ma_uint32)sampleRate;
41795 ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorPlayback->channelMap, ma_countof(pParameters->pDescriptorPlayback->channelMap), pParameters->pDescriptorPlayback->channels);
41796 pParameters->pDescriptorPlayback->periodSizeInFrames = intermediaryBufferSizeInFrames;
41797 pParameters->pDescriptorPlayback->periodCount = 1;
41798 }
41799
41800 /* At this point we're done and we can return. */
41801 ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", pParameters->pDevice->webaudio.audioWorklet);
41802 pParameters->pDevice->webaudio.initResult = MA_SUCCESS;
41803 ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);
41804}
41805
41806static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData)
41807{
41808 ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData;
41809 WebAudioWorkletProcessorCreateOptions workletProcessorOptions;
41810
41811 MA_ASSERT(pParameters != NULL);
41812
41813 if (success == EM_FALSE) {
41814 pParameters->pDevice->webaudio.initResult = MA_ERROR;
41815 return;
41816 }
41817
41818 MA_ZERO_OBJECT(&workletProcessorOptions);
41819 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? */
41820
41821 emscripten_create_wasm_audio_worklet_processor_async(audioContext, &workletProcessorOptions, ma_audio_worklet_processor_created__webaudio, pParameters);
41822}
41823#endif
41824
41825static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
41826{
41827 if (pConfig->deviceType == ma_device_type_loopback) {
41829 }
41830
41831 /* No exclusive mode with Web Audio. */
41832 if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
41833 ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
41835 }
41836
41837 /*
41838 With AudioWorklets we'll have just a single AudioContext. I'm not sure why I'm not doing this for ScriptProcessorNode so
41839 it might be worthwhile to look into that as well.
41840 */
41841 #if defined(MA_USE_AUDIO_WORKLETS)
41842 {
41843 EmscriptenWebAudioCreateAttributes audioContextAttributes;
41844 ma_audio_worklet_thread_initialized_data* pInitParameters;
41845 void* pStackBuffer;
41846
41848 audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_PLAYBACK;
41849 } else {
41850 audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE;
41851 }
41852
41853 /*
41854 In my testing, Firefox does not seem to capture audio data properly if the sample rate is set
41855 to anything other than 48K. This does not seem to be the case for other browsers. For this reason,
41856 if the device type is anything other than playback, we'll leave the sample rate as-is and let the
41857 browser pick the appropriate rate for us.
41858 */
41859 if (pConfig->deviceType == ma_device_type_playback) {
41860 audioContextAttributes.sampleRate = pDescriptorPlayback->sampleRate;
41861 } else {
41862 audioContextAttributes.sampleRate = 0;
41863 }
41864
41865 /* 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. */
41866 pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes);
41867
41868 /*
41869 With the context created we can now create the worklet. We can only have a single worklet per audio
41870 context which means we'll need to craft this appropriately to handle duplex devices correctly.
41871 */
41872
41873 /*
41874 We now need to create a worker thread. This is a bit weird because we need to allocate our
41875 own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to
41876 allocate this on the heap to keep it simple.
41877 */
41878 pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks);
41879 if (pStackBuffer == NULL) {
41880 emscripten_destroy_audio_context(pDevice->webaudio.audioContext);
41881 return MA_OUT_OF_MEMORY;
41882 }
41883
41884 /* Our thread initialization parameters need to be allocated on the heap so they don't go out of scope. */
41885 pInitParameters = (ma_audio_worklet_thread_initialized_data*)ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks);
41886 if (pInitParameters == NULL) {
41887 ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks);
41888 emscripten_destroy_audio_context(pDevice->webaudio.audioContext);
41889 return MA_OUT_OF_MEMORY;
41890 }
41891
41892 pInitParameters->pDevice = pDevice;
41893 pInitParameters->pConfig = pConfig;
41894 pInitParameters->pDescriptorPlayback = pDescriptorPlayback;
41895 pInitParameters->pDescriptorCapture = pDescriptorCapture;
41896
41897 /*
41898 We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of
41899 the Emscripten WebAudio stuff is asynchronous.
41900 */
41901 pDevice->webaudio.initResult = MA_BUSY;
41902 {
41903 emscripten_start_wasm_audio_worklet_thread_async(pDevice->webaudio.audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters);
41904 }
41905 while (pDevice->webaudio.initResult == MA_BUSY) { emscripten_sleep(1); } /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */
41906
41907 /* Initialization is now complete. Descriptors were updated when the worklet was initialized. */
41908 if (pDevice->webaudio.initResult != MA_SUCCESS) {
41909 ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks);
41910 emscripten_destroy_audio_context(pDevice->webaudio.audioContext);
41911 return pDevice->webaudio.initResult;
41912 }
41913
41914 /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */
41915 pDevice->webaudio.deviceIndex = EM_ASM_INT({
41916 return window.miniaudio.track_device({
41917 webaudio: emscriptenGetAudioObject($0),
41918 state: 1, /* 1 = ma_device_state_stopped */
41919 pDevice: $1
41920 });
41921 }, pDevice->webaudio.audioContext, pDevice);
41922
41923 return MA_SUCCESS;
41924 }
41925 #else
41926 {
41927 /* ScriptProcessorNode. This path requires us to do almost everything in JS, but we'll do as much as we can in C. */
41928 ma_uint32 deviceIndex;
41929 ma_uint32 channels;
41930 ma_uint32 sampleRate;
41931 ma_uint32 periodSizeInFrames;
41932
41933 /* The channel count will depend on the device type. If it's a capture, use its, otherwise use the playback side. */
41934 if (pConfig->deviceType == ma_device_type_capture) {
41935 channels = (pDescriptorCapture->channels > 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS;
41936 } else {
41937 channels = (pDescriptorPlayback->channels > 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS;
41938 }
41939
41940 /*
41941 When testing in Firefox, I've seen it where capture mode fails if the sample rate is changed to anything other than it's
41942 native rate. For this reason we're leaving the sample rate untouched for capture devices.
41943 */
41944 if (pConfig->deviceType == ma_device_type_playback) {
41945 sampleRate = pDescriptorPlayback->sampleRate;
41946 } else {
41947 sampleRate = 0; /* Let the browser decide when capturing. */
41948 }
41949
41950 /* The period size needs to be a power of 2. */
41951 if (pConfig->deviceType == ma_device_type_capture) {
41952 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorCapture, sampleRate, pConfig->performanceProfile);
41953 } else {
41954 periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorPlayback, sampleRate, pConfig->performanceProfile);
41955 }
41956
41957 /* We need an intermediary buffer for doing interleaving and deinterleaving. */
41958 pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(periodSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks);
41959 if (pDevice->webaudio.pIntermediaryBuffer == NULL) {
41960 return MA_OUT_OF_MEMORY;
41961 }
41962
41963 deviceIndex = EM_ASM_INT({
41964 var deviceType = $0;
41965 var channels = $1;
41966 var sampleRate = $2;
41967 var bufferSize = $3;
41968 var pIntermediaryBuffer = $4;
41969 var pDevice = $5;
41970
41971 if (typeof(window.miniaudio) === 'undefined') {
41972 return -1; /* Context not initialized. */
41973 }
41974
41975 var device = {};
41976
41977 /* First thing we need is an AudioContext. */
41978 var audioContextOptions = {};
41979 if (deviceType == window.miniaudio.device_type.playback && sampleRate != 0) {
41980 audioContextOptions.sampleRate = sampleRate;
41981 }
41982
41983 device.webaudio = new (window.AudioContext || window.webkitAudioContext)(audioContextOptions);
41984 device.webaudio.suspend(); /* The AudioContext must be created in a suspended state. */
41985 device.state = window.miniaudio.device_state.stopped;
41986
41987 /*
41988 We need to create a ScriptProcessorNode. The channel situation is the same as the AudioWorklet path in that we
41989 need to specify an output and configure the channel count there.
41990 */
41991 var channelCountIn = 0;
41992 var channelCountOut = channels;
41993 if (deviceType != window.miniaudio.device_type.playback) {
41994 channelCountIn = channels;
41995 }
41996
41997 device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channelCountIn, channelCountOut);
41998
41999 /* The node processing callback. */
42000 device.scriptNode.onaudioprocess = function(e) {
42001 if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) {
42002 device.intermediaryBufferView = new Float32Array(HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels);
42003 }
42004
42005 /* Do the capture side first. */
42006 if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) {
42007 /* The data must be interleaved before being processed miniaudio. */
42008 for (var iChannel = 0; iChannel < channels; iChannel += 1) {
42009 var inputBuffer = e.inputBuffer.getChannelData(iChannel);
42010 var intermediaryBuffer = device.intermediaryBufferView;
42011
42012 for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) {
42013 intermediaryBuffer[iFrame*channels + iChannel] = inputBuffer[iFrame];
42014 }
42015 }
42016
42017 _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer);
42018 }
42019
42020 if (deviceType == window.miniaudio.device_type.playback || deviceType == window.miniaudio.device_type.duplex) {
42021 _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer);
42022
42023 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
42024 var outputBuffer = e.outputBuffer.getChannelData(iChannel);
42025 var intermediaryBuffer = device.intermediaryBufferView;
42026
42027 for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) {
42028 outputBuffer[iFrame] = intermediaryBuffer[iFrame*channels + iChannel];
42029 }
42030 }
42031 } else {
42032 /* It's a capture-only device. Make sure the output is silenced. */
42033 for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
42034 e.outputBuffer.getChannelData(iChannel).fill(0.0);
42035 }
42036 }
42037 };
42038
42039 /* Now we need to connect our node to the graph. */
42040 if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) {
42041 navigator.mediaDevices.getUserMedia({audio:true, video:false})
42042 .then(function(stream) {
42043 device.streamNode = device.webaudio.createMediaStreamSource(stream);
42044 device.streamNode.connect(device.scriptNode);
42045 device.scriptNode.connect(device.webaudio.destination);
42046 })
42047 .catch(function(error) {
42048 console.log("Failed to get user media: " + error);
42049 });
42050 }
42051
42052 if (deviceType == window.miniaudio.device_type.playback) {
42053 device.scriptNode.connect(device.webaudio.destination);
42054 }
42055
42056 device.pDevice = pDevice;
42057
42058 return window.miniaudio.track_device(device);
42059 }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice);
42060
42061 if (deviceIndex < 0) {
42063 }
42064
42065 pDevice->webaudio.deviceIndex = deviceIndex;
42066
42067 /* Grab the sample rate from the audio context directly. */
42068 sampleRate = (ma_uint32)EM_ASM_INT({ return window.miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
42069
42070 if (pDescriptorCapture != NULL) {
42071 pDescriptorCapture->format = ma_format_f32;
42072 pDescriptorCapture->channels = channels;
42073 pDescriptorCapture->sampleRate = sampleRate;
42074 ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
42075 pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
42076 pDescriptorCapture->periodCount = 1;
42077 }
42078
42079 if (pDescriptorPlayback != NULL) {
42080 pDescriptorPlayback->format = ma_format_f32;
42081 pDescriptorPlayback->channels = channels;
42082 pDescriptorPlayback->sampleRate = sampleRate;
42083 ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);
42084 pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
42085 pDescriptorPlayback->periodCount = 1;
42086 }
42087
42088 return MA_SUCCESS;
42089 }
42090 #endif
42091}
42092
42093static ma_result ma_device_start__webaudio(ma_device* pDevice)
42094{
42095 MA_ASSERT(pDevice != NULL);
42096
42097 EM_ASM({
42098 var device = window.miniaudio.get_device_by_index($0);
42099 device.webaudio.resume();
42100 device.state = window.miniaudio.device_state.started;
42101 }, pDevice->webaudio.deviceIndex);
42102
42103 return MA_SUCCESS;
42104}
42105
42106static ma_result ma_device_stop__webaudio(ma_device* pDevice)
42107{
42108 MA_ASSERT(pDevice != NULL);
42109
42110 /*
42111 From the WebAudio API documentation for AudioContext.suspend():
42112
42113 Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the
42114 destination, and then allows the system to release its claim on audio hardware.
42115
42116 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
42117 do any kind of explicit draining.
42118 */
42119 EM_ASM({
42120 var device = window.miniaudio.get_device_by_index($0);
42121 device.webaudio.suspend();
42122 device.state = window.miniaudio.device_state.stopped;
42123 }, pDevice->webaudio.deviceIndex);
42124
42125 ma_device__on_notification_stopped(pDevice);
42126
42127 return MA_SUCCESS;
42128}
42129
42130static ma_result ma_context_uninit__webaudio(ma_context* pContext)
42131{
42132 MA_ASSERT(pContext != NULL);
42133 MA_ASSERT(pContext->backend == ma_backend_webaudio);
42134
42135 (void)pContext; /* Unused. */
42136
42137 /* Remove the global miniaudio object from window if there are no more references to it. */
42138 EM_ASM({
42139 if (typeof(window.miniaudio) !== 'undefined') {
42140 miniaudio.unlock_event_types.map(function(event_type) {
42141 document.removeEventListener(event_type, miniaudio.unlock, true);
42142 });
42143
42144 window.miniaudio.referenceCount -= 1;
42145 if (window.miniaudio.referenceCount === 0) {
42146 delete window.miniaudio;
42147 }
42148 }
42149 });
42150
42151 return MA_SUCCESS;
42152}
42153
42154static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
42155{
42156 int resultFromJS;
42157
42158 MA_ASSERT(pContext != NULL);
42159
42160 (void)pConfig; /* Unused. */
42161
42162 /* Here is where our global JavaScript object is initialized. */
42163 resultFromJS = EM_ASM_INT({
42164 if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) {
42165 return 0; /* Web Audio not supported. */
42166 }
42167
42168 if (typeof(window.miniaudio) === 'undefined') {
42169 window.miniaudio = {
42170 referenceCount: 0
42171 };
42172
42173 /* Device types. */
42174 window.miniaudio.device_type = {};
42175 window.miniaudio.device_type.playback = $0;
42176 window.miniaudio.device_type.capture = $1;
42177 window.miniaudio.device_type.duplex = $2;
42178
42179 /* Device states. */
42180 window.miniaudio.device_state = {};
42181 window.miniaudio.device_state.stopped = $3;
42182 window.miniaudio.device_state.started = $4;
42183
42184 /* Device cache for mapping devices to indexes for JavaScript/C interop. */
42185 let miniaudio = window.miniaudio;
42186 miniaudio.devices = [];
42187
42188 miniaudio.track_device = function(device) {
42189 /* Try inserting into a free slot first. */
42190 for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
42191 if (miniaudio.devices[iDevice] == null) {
42192 miniaudio.devices[iDevice] = device;
42193 return iDevice;
42194 }
42195 }
42196
42197 /* Getting here means there is no empty slots in the array so we just push to the end. */
42198 miniaudio.devices.push(device);
42199 return miniaudio.devices.length - 1;
42200 };
42201
42202 miniaudio.untrack_device_by_index = function(deviceIndex) {
42203 /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */
42204 miniaudio.devices[deviceIndex] = null;
42205
42206 /* Trim the array if possible. */
42207 while (miniaudio.devices.length > 0) {
42208 if (miniaudio.devices[miniaudio.devices.length-1] == null) {
42209 miniaudio.devices.pop();
42210 } else {
42211 break;
42212 }
42213 }
42214 };
42215
42216 miniaudio.untrack_device = function(device) {
42217 for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
42218 if (miniaudio.devices[iDevice] == device) {
42219 return miniaudio.untrack_device_by_index(iDevice);
42220 }
42221 }
42222 };
42223
42224 miniaudio.get_device_by_index = function(deviceIndex) {
42225 return miniaudio.devices[deviceIndex];
42226 };
42227
42228 miniaudio.unlock_event_types = (function(){
42229 return ['$o0', '$w35'];
42230 })();
42231
42232 miniaudio.unlock = function() {
42233 for(var i = 0; i < miniaudio.devices.length; ++i) {
42234 var device = miniaudio.devices[i];
42235 if (device != null &&
42236 device.webaudio != null &&
42237 device.state === miniaudio.device_state.started) {
42238
42239 device.webaudio.resume().then(() => {
42240 _ma_device__on_notification_unlocked(device.pDevice);
42241 },
42242 (error) => {console.error("Failed to resume audiocontext", error);
42243 });
42244 }
42245 }
42246 miniaudio.unlock_event_types.map(function(event_type) {
42247 document.removeEventListener(event_type, miniaudio.unlock, true);
42248 });
42249 };
42250
42251 miniaudio.unlock_event_types.map(function(event_type) {
42252 document.addEventListener(event_type, miniaudio.unlock, true);
42253 });
42254 }
42255
42256 window.miniaudio.referenceCount += 1;
42257
42258 return 1;
42260
42261 if (resultFromJS != 1) {
42263 }
42264
42265 pCallbacks->onContextInit = ma_context_init__webaudio;
42266 pCallbacks->onContextUninit = ma_context_uninit__webaudio;
42267 pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio;
42268 pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio;
42269 pCallbacks->onDeviceInit = ma_device_init__webaudio;
42270 pCallbacks->onDeviceUninit = ma_device_uninit__webaudio;
42271 pCallbacks->onDeviceStart = ma_device_start__webaudio;
42272 pCallbacks->onDeviceStop = ma_device_stop__webaudio;
42273 pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */
42274 pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */
42275 pCallbacks->onDeviceDataLoop = NULL; /* Not needed because WebAudio is asynchronous. */
42276
42277 return MA_SUCCESS;
42278}
42279#endif /* MA_HAS_WEBAUDIO */
42280
42281
42282
42283static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint32 channels)
42284{
42285 /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */
42286 if (pChannelMap != NULL && pChannelMap[0] != MA_CHANNEL_NONE) {
42287 ma_uint32 iChannel;
42288
42289 if (channels == 0 || channels > MA_MAX_CHANNELS) {
42290 return MA_FALSE; /* Channel count out of range. */
42291 }
42292
42293 /* A channel cannot be present in the channel map more than once. */
42294 for (iChannel = 0; iChannel < channels; ++iChannel) {
42295 ma_uint32 jChannel;
42296 for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) {
42297 if (pChannelMap[iChannel] == pChannelMap[jChannel]) {
42298 return MA_FALSE;
42299 }
42300 }
42301 }
42302 }
42303
42304 return MA_TRUE;
42305}
42306
42307
42308static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext)
42309{
42310 MA_ASSERT(pContext != NULL);
42311
42312 if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) {
42313 if (pContext->callbacks.onDeviceDataLoop == NULL) {
42314 return MA_TRUE;
42315 } else {
42316 return MA_FALSE;
42317 }
42318 } else {
42319 return MA_FALSE;
42320 }
42321}
42322
42323
42324static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType)
42325{
42326 ma_result result;
42327
42328 MA_ASSERT(pDevice != NULL);
42329
42330 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
42331 if (pDevice->capture.format == ma_format_unknown) {
42332 pDevice->capture.format = pDevice->capture.internalFormat;
42333 }
42334 if (pDevice->capture.channels == 0) {
42335 pDevice->capture.channels = pDevice->capture.internalChannels;
42336 }
42337 if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) {
42338 MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS);
42339 if (pDevice->capture.internalChannels == pDevice->capture.channels) {
42341 } else {
42344 } else {
42346 }
42347 }
42348 }
42349 }
42350
42351 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
42352 if (pDevice->playback.format == ma_format_unknown) {
42353 pDevice->playback.format = pDevice->playback.internalFormat;
42354 }
42355 if (pDevice->playback.channels == 0) {
42356 pDevice->playback.channels = pDevice->playback.internalChannels;
42357 }
42358 if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) {
42359 MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS);
42360 if (pDevice->playback.internalChannels == pDevice->playback.channels) {
42362 } else {
42365 } else {
42367 }
42368 }
42369 }
42370 }
42371
42372 if (pDevice->sampleRate == 0) {
42373 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
42374 pDevice->sampleRate = pDevice->capture.internalSampleRate;
42375 } else {
42376 pDevice->sampleRate = pDevice->playback.internalSampleRate;
42377 }
42378 }
42379
42380 /* Data converters. */
42381 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
42382 /* Converting from internal device format to client format. */
42384 converterConfig.formatIn = pDevice->capture.internalFormat;
42385 converterConfig.channelsIn = pDevice->capture.internalChannels;
42386 converterConfig.sampleRateIn = pDevice->capture.internalSampleRate;
42387 converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap;
42388 converterConfig.formatOut = pDevice->capture.format;
42389 converterConfig.channelsOut = pDevice->capture.channels;
42390 converterConfig.sampleRateOut = pDevice->sampleRate;
42391 converterConfig.pChannelMapOut = pDevice->capture.channelMap;
42392 converterConfig.channelMixMode = pDevice->capture.channelMixMode;
42394 converterConfig.allowDynamicSampleRate = MA_FALSE;
42395 converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
42396 converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
42397 converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable;
42398 converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData;
42399
42400 /* Make sure the old converter is uninitialized first. */
42403 }
42404
42405 result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter);
42406 if (result != MA_SUCCESS) {
42407 return result;
42408 }
42409 }
42410
42411 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
42412 /* Converting from client format to device format. */
42414 converterConfig.formatIn = pDevice->playback.format;
42415 converterConfig.channelsIn = pDevice->playback.channels;
42416 converterConfig.sampleRateIn = pDevice->sampleRate;
42417 converterConfig.pChannelMapIn = pDevice->playback.channelMap;
42418 converterConfig.formatOut = pDevice->playback.internalFormat;
42419 converterConfig.channelsOut = pDevice->playback.internalChannels;
42420 converterConfig.sampleRateOut = pDevice->playback.internalSampleRate;
42421 converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap;
42422 converterConfig.channelMixMode = pDevice->playback.channelMixMode;
42424 converterConfig.allowDynamicSampleRate = MA_FALSE;
42425 converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
42426 converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
42427 converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable;
42428 converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData;
42429
42430 /* Make sure the old converter is uninitialized first. */
42433 }
42434
42435 result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter);
42436 if (result != MA_SUCCESS) {
42437 return result;
42438 }
42439 }
42440
42441
42442 /*
42443 If the device is doing playback (ma_device_type_playback or ma_device_type_duplex), there's
42444 a couple of situations where we'll need a heap allocated cache.
42445
42446 The first is a duplex device for backends that use a callback for data delivery. The reason
42447 this is needed is that the input stage needs to have a buffer to place the input data while it
42448 waits for the playback stage, after which the miniaudio data callback will get fired. This is
42449 not needed for backends that use a blocking API because miniaudio manages temporary buffers on
42450 the stack to achieve this.
42451
42452 The other situation is when the data converter does not have the ability to query the number
42453 of input frames that are required in order to process a given number of output frames. When
42454 performing data conversion, it's useful if miniaudio know exactly how many frames it needs
42455 from the client in order to generate a given number of output frames. This way, only exactly
42456 the number of frames are needed to be read from the client which means no cache is necessary.
42457 On the other hand, if miniaudio doesn't know how many frames to read, it is forced to read
42458 in fixed sized chunks and then cache any residual unused input frames, those of which will be
42459 processed at a later stage.
42460 */
42461 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
42463
42464 pDevice->playback.inputCacheConsumed = 0;
42465 pDevice->playback.inputCacheRemaining = 0;
42466
42467 if (pDevice->type == ma_device_type_duplex || /* Duplex. backend may decide to use ma_device_handle_backend_data_callback() which will require this cache. */
42468 ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) /* Data conversion required input frame calculation not supported. */
42469 {
42470 /* We need a heap allocated cache. We want to size this based on the period size. */
42471 void* pNewInputCache;
42472 ma_uint64 newInputCacheCap;
42473 ma_uint64 newInputCacheSizeInBytes;
42474
42475 newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames);
42476
42477 newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
42478 if (newInputCacheSizeInBytes > MA_SIZE_MAX) {
42480 pDevice->playback.pInputCache = NULL;
42481 pDevice->playback.inputCacheCap = 0;
42482 return MA_OUT_OF_MEMORY; /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */
42483 }
42484
42485 pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks);
42486 if (pNewInputCache == NULL) {
42488 pDevice->playback.pInputCache = NULL;
42489 pDevice->playback.inputCacheCap = 0;
42490 return MA_OUT_OF_MEMORY;
42491 }
42492
42493 pDevice->playback.pInputCache = pNewInputCache;
42494 pDevice->playback.inputCacheCap = newInputCacheCap;
42495 } else {
42496 /* 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. */
42498 pDevice->playback.pInputCache = NULL;
42499 pDevice->playback.inputCacheCap = 0;
42500 }
42501 }
42502
42503 return MA_SUCCESS;
42504}
42505
42506MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pDescriptorPlayback, const ma_device_descriptor* pDescriptorCapture)
42507{
42508 ma_result result;
42509
42510 if (pDevice == NULL) {
42511 return MA_INVALID_ARGS;
42512 }
42513
42514 /* Capture. */
42515 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
42516 if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) {
42517 return MA_INVALID_ARGS;
42518 }
42519
42520 pDevice->capture.internalFormat = pDescriptorCapture->format;
42521 pDevice->capture.internalChannels = pDescriptorCapture->channels;
42522 pDevice->capture.internalSampleRate = pDescriptorCapture->sampleRate;
42523 MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
42524 pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
42525 pDevice->capture.internalPeriods = pDescriptorCapture->periodCount;
42526
42527 if (pDevice->capture.internalPeriodSizeInFrames == 0) {
42529 }
42530 }
42531
42532 /* Playback. */
42533 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
42534 if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) {
42535 return MA_INVALID_ARGS;
42536 }
42537
42538 pDevice->playback.internalFormat = pDescriptorPlayback->format;
42539 pDevice->playback.internalChannels = pDescriptorPlayback->channels;
42540 pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate;
42541 MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
42542 pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
42543 pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount;
42544
42545 if (pDevice->playback.internalPeriodSizeInFrames == 0) {
42547 }
42548 }
42549
42550 /*
42551 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.
42552 For loopback devices, we need to retrieve the name of the playback device.
42553 */
42554 {
42555 ma_device_info deviceInfo;
42556
42557 if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
42558 result = ma_device_get_info(pDevice, ma_device_type_capture, &deviceInfo);
42559 if (result == MA_SUCCESS) {
42560 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);
42561 } else {
42562 /* We failed to retrieve the device info. Fall back to a default name. */
42563 if (pDescriptorCapture->pDeviceID == NULL) {
42564 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
42565 } else {
42566 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1);
42567 }
42568 }
42569 }
42570
42571 if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
42572 result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo);
42573 if (result == MA_SUCCESS) {
42574 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1);
42575 } else {
42576 /* We failed to retrieve the device info. Fall back to a default name. */
42577 if (pDescriptorPlayback->pDeviceID == NULL) {
42578 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
42579 } else {
42580 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1);
42581 }
42582 }
42583 }
42584 }
42585
42586 /* Update data conversion. */
42587 return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */
42588}
42589
42590
42591static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
42592{
42593 ma_device* pDevice = (ma_device*)pData;
42594#ifdef MA_WIN32
42595 HRESULT CoInitializeResult;
42596#endif
42597
42598 MA_ASSERT(pDevice != NULL);
42599
42600#ifdef MA_WIN32
42601 CoInitializeResult = ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE);
42602#endif
42603
42604 /*
42605 When the device is being initialized its initial state is set to ma_device_state_uninitialized. Before returning from
42606 ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately
42607 after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker
42608 thread to signal an event to know when the worker thread is ready for action.
42609 */
42610 ma_device__set_state(pDevice, ma_device_state_stopped);
42611 ma_event_signal(&pDevice->stopEvent);
42612
42613 for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */
42614 ma_result startResult;
42615 ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */
42616
42617 /* We wait on an event to know when something has requested that the device be started and the main loop entered. */
42618 ma_event_wait(&pDevice->wakeupEvent);
42619
42620 /* Default result code. */
42621 pDevice->workResult = MA_SUCCESS;
42622
42623 /* If the reason for the wake up is that we are terminating, just break from the loop. */
42625 break;
42626 }
42627
42628 /*
42629 Getting to this point means the device is wanting to get started. The function that has requested that the device
42630 be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event
42631 in both the success and error case. It's important that the state of the device is set _before_ signaling the event.
42632 */
42633 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting);
42634
42635 /* If the device has a start callback, start it now. */
42636 if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
42637 startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice);
42638 } else {
42639 startResult = MA_SUCCESS;
42640 }
42641
42642 /*
42643 If starting was not successful we'll need to loop back to the start and wait for something
42644 to happen (pDevice->wakeupEvent).
42645 */
42646 if (startResult != MA_SUCCESS) {
42647 pDevice->workResult = startResult;
42648 ma_event_signal(&pDevice->startEvent); /* <-- Always signal the start event so ma_device_start() can return as it'll be waiting on it. */
42649 continue;
42650 }
42651
42652 /* Make sure the state is set appropriately. */
42653 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. */
42654 ma_event_signal(&pDevice->startEvent);
42655
42656 ma_device__on_notification_started(pDevice);
42657
42658 if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) {
42659 pDevice->pContext->callbacks.onDeviceDataLoop(pDevice);
42660 } else {
42661 /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */
42662 ma_device_audio_thread__default_read_write(pDevice);
42663 }
42664
42665 /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */
42666 if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
42667 stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice);
42668 } else {
42669 stopResult = MA_SUCCESS; /* No stop callback with the backend. Just assume successful. */
42670 }
42671
42672 /*
42673 After the device has stopped, make sure an event is posted. Don't post a stopped event if
42674 stopping failed. This can happen on some backends when the underlying stream has been
42675 stopped due to the device being physically unplugged or disabled via an OS setting.
42676 */
42677 if (stopResult == MA_SUCCESS) {
42678 ma_device__on_notification_stopped(pDevice);
42679 }
42680
42681 /* If we stopped because the device has been uninitialized, abort now. */
42683 break;
42684 }
42685
42686 /* 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. */
42687 ma_device__set_state(pDevice, ma_device_state_stopped);
42688 ma_event_signal(&pDevice->stopEvent);
42689 }
42690
42691#ifdef MA_WIN32
42692 if (CoInitializeResult == S_OK || CoInitializeResult == S_FALSE) {
42693 ma_CoUninitialize(pDevice->pContext);
42694 }
42695#endif
42696
42697 return (ma_thread_result)0;
42698}
42699
42700
42701/* Helper for determining whether or not the given device is initialized. */
42702static ma_bool32 ma_device__is_initialized(ma_device* pDevice)
42703{
42704 if (pDevice == NULL) {
42705 return MA_FALSE;
42706 }
42707
42709}
42710
42711
42712#ifdef MA_WIN32
42713static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext)
42714{
42715 /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */
42716#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
42717 if (pContext->win32.CoInitializeResult == S_OK || pContext->win32.CoInitializeResult == S_FALSE) {
42718 ma_CoUninitialize(pContext);
42719 }
42720
42721 #if defined(MA_WIN32_DESKTOP)
42722 ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL);
42723 ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL);
42724 #endif
42725
42726 ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL);
42727#else
42728 (void)pContext;
42729#endif
42730
42731 return MA_SUCCESS;
42732}
42733
42734static ma_result ma_context_init_backend_apis__win32(ma_context* pContext)
42735{
42736#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
42737 #if defined(MA_WIN32_DESKTOP)
42738 /* User32.dll */
42739 pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll");
42740 if (pContext->win32.hUser32DLL == NULL) {
42742 }
42743
42744 pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow");
42745 pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow");
42746
42747
42748 /* Advapi32.dll */
42749 pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll");
42750 if (pContext->win32.hAdvapi32DLL == NULL) {
42752 }
42753
42754 pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA");
42755 pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey");
42756 pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA");
42757 #endif
42758
42759 /* Ole32.dll */
42760 pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll");
42761 if (pContext->win32.hOle32DLL == NULL) {
42763 }
42764
42765 pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize");
42766 pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx");
42767 pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize");
42768 pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance");
42769 pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree");
42770 pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear");
42771 pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2");
42772#else
42773 (void)pContext; /* Unused. */
42774#endif
42775
42776 pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);
42777 return MA_SUCCESS;
42778}
42779#else
42780static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext)
42781{
42782 (void)pContext;
42783
42784 return MA_SUCCESS;
42785}
42786
42787static ma_result ma_context_init_backend_apis__nix(ma_context* pContext)
42788{
42789 (void)pContext;
42790
42791 return MA_SUCCESS;
42792}
42793#endif
42794
42795static ma_result ma_context_init_backend_apis(ma_context* pContext)
42796{
42797 ma_result result;
42798#ifdef MA_WIN32
42799 result = ma_context_init_backend_apis__win32(pContext);
42800#else
42801 result = ma_context_init_backend_apis__nix(pContext);
42802#endif
42803
42804 return result;
42805}
42806
42807static ma_result ma_context_uninit_backend_apis(ma_context* pContext)
42808{
42809 ma_result result;
42810#ifdef MA_WIN32
42811 result = ma_context_uninit_backend_apis__win32(pContext);
42812#else
42813 result = ma_context_uninit_backend_apis__nix(pContext);
42814#endif
42815
42816 return result;
42817}
42818
42819
42820/* The default capacity doesn't need to be too big. */
42821#ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY
42822#define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32
42823#endif
42824
42826{
42828
42829 MA_ZERO_OBJECT(&config);
42830 config.noThread = MA_FALSE;
42831 config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY;
42832 config.jobQueueFlags = 0;
42833
42834 return config;
42835}
42836
42837
42838static ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData)
42839{
42840 ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData;
42841 MA_ASSERT(pJobThread != NULL);
42842
42843 for (;;) {
42844 ma_result result;
42845 ma_job job;
42846
42847 result = ma_device_job_thread_next(pJobThread, &job);
42848 if (result != MA_SUCCESS) {
42849 break;
42850 }
42851
42852 if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) {
42853 break;
42854 }
42855
42856 ma_job_process(&job);
42857 }
42858
42859 return (ma_thread_result)0;
42860}
42861
42863{
42864 ma_result result;
42865 ma_job_queue_config jobQueueConfig;
42866
42867 if (pJobThread == NULL) {
42868 return MA_INVALID_ARGS;
42869 }
42870
42871 MA_ZERO_OBJECT(pJobThread);
42872
42873 if (pConfig == NULL) {
42874 return MA_INVALID_ARGS;
42875 }
42876
42877
42878 /* Initialize the job queue before the thread to ensure it's in a valid state. */
42879 jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity);
42880
42881 result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue);
42882 if (result != MA_SUCCESS) {
42883 return result; /* Failed to initialize job queue. */
42884 }
42885
42886
42887 /* The thread needs to be initialized after the job queue to ensure the thread doesn't try to access it prematurely. */
42888 if (pConfig->noThread == MA_FALSE) {
42889 result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks);
42890 if (result != MA_SUCCESS) {
42891 ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks);
42892 return result; /* Failed to create the job thread. */
42893 }
42894
42895 pJobThread->_hasThread = MA_TRUE;
42896 } else {
42897 pJobThread->_hasThread = MA_FALSE;
42898 }
42899
42900
42901 return MA_SUCCESS;
42902}
42903
42904MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks)
42905{
42906 if (pJobThread == NULL) {
42907 return;
42908 }
42909
42910 /* 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. */
42911 {
42913 ma_device_job_thread_post(pJobThread, &job);
42914 }
42915
42916 /* Wait for the thread to terminate naturally. */
42917 if (pJobThread->_hasThread) {
42918 ma_thread_wait(&pJobThread->thread);
42919 }
42920
42921 /* At this point the thread should be terminated so we can safely uninitialize the job queue. */
42922 ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks);
42923}
42924
42926{
42927 if (pJobThread == NULL || pJob == NULL) {
42928 return MA_INVALID_ARGS;
42929 }
42930
42931 return ma_job_queue_post(&pJobThread->jobQueue, pJob);
42932}
42933
42935{
42936 if (pJob == NULL) {
42937 return MA_INVALID_ARGS;
42938 }
42939
42940 MA_ZERO_OBJECT(pJob);
42941
42942 if (pJobThread == NULL) {
42943 return MA_INVALID_ARGS;
42944 }
42945
42946 return ma_job_queue_next(&pJobThread->jobQueue, pJob);
42947}
42948
42949
42951{
42952 size_t i;
42953
42954 if (pA == NULL || pB == NULL) {
42955 return MA_FALSE;
42956 }
42957
42958 for (i = 0; i < sizeof(ma_device_id); i += 1) {
42959 if (((const char*)pA)[i] != ((const char*)pB)[i]) {
42960 return MA_FALSE;
42961 }
42962 }
42963
42964 return MA_TRUE;
42965}
42966
42967
42968
42970{
42971 ma_context_config config;
42972 MA_ZERO_OBJECT(&config);
42973
42974 return config;
42975}
42976
42977MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext)
42978{
42979 ma_result result;
42980 ma_context_config defaultConfig;
42981 ma_backend defaultBackends[ma_backend_null+1];
42982 ma_uint32 iBackend;
42983 ma_backend* pBackendsToIterate;
42984 ma_uint32 backendsToIterateCount;
42985
42986 if (pContext == NULL) {
42987 return MA_INVALID_ARGS;
42988 }
42989
42990 MA_ZERO_OBJECT(pContext);
42991
42992 /* Always make sure the config is set first to ensure properties are available as soon as possible. */
42993 if (pConfig == NULL) {
42994 defaultConfig = ma_context_config_init();
42995 pConfig = &defaultConfig;
42996 }
42997
42998 /* Allocation callbacks need to come first because they'll be passed around to other areas. */
42999 result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks);
43000 if (result != MA_SUCCESS) {
43001 return result;
43002 }
43003
43004 /* Get a lot set up first so we can start logging ASAP. */
43005 if (pConfig->pLog != NULL) {
43006 pContext->pLog = pConfig->pLog;
43007 } else {
43008 result = ma_log_init(&pContext->allocationCallbacks, &pContext->log);
43009 if (result == MA_SUCCESS) {
43010 pContext->pLog = &pContext->log;
43011 } else {
43012 pContext->pLog = NULL; /* Logging is not available. */
43013 }
43014 }
43015
43016 pContext->threadPriority = pConfig->threadPriority;
43017 pContext->threadStackSize = pConfig->threadStackSize;
43018 pContext->pUserData = pConfig->pUserData;
43019
43020 /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */
43021 result = ma_context_init_backend_apis(pContext);
43022 if (result != MA_SUCCESS) {
43023 return result;
43024 }
43025
43026 for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
43027 defaultBackends[iBackend] = (ma_backend)iBackend;
43028 }
43029
43030 pBackendsToIterate = (ma_backend*)backends;
43031 backendsToIterateCount = backendCount;
43032 if (pBackendsToIterate == NULL) {
43033 pBackendsToIterate = (ma_backend*)defaultBackends;
43034 backendsToIterateCount = ma_countof(defaultBackends);
43035 }
43036
43037 MA_ASSERT(pBackendsToIterate != NULL);
43038
43039 for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) {
43040 ma_backend backend = pBackendsToIterate[iBackend];
43041
43042 /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */
43043 MA_ZERO_OBJECT(&pContext->callbacks);
43044
43045 /* These backends are using the new callback system. */
43046 switch (backend) {
43047 #ifdef MA_HAS_WASAPI
43048 case ma_backend_wasapi:
43049 {
43050 pContext->callbacks.onContextInit = ma_context_init__wasapi;
43051 } break;
43052 #endif
43053 #ifdef MA_HAS_DSOUND
43054 case ma_backend_dsound:
43055 {
43056 pContext->callbacks.onContextInit = ma_context_init__dsound;
43057 } break;
43058 #endif
43059 #ifdef MA_HAS_WINMM
43060 case ma_backend_winmm:
43061 {
43062 pContext->callbacks.onContextInit = ma_context_init__winmm;
43063 } break;
43064 #endif
43065 #ifdef MA_HAS_COREAUDIO
43067 {
43068 pContext->callbacks.onContextInit = ma_context_init__coreaudio;
43069 } break;
43070 #endif
43071 #ifdef MA_HAS_SNDIO
43072 case ma_backend_sndio:
43073 {
43074 pContext->callbacks.onContextInit = ma_context_init__sndio;
43075 } break;
43076 #endif
43077 #ifdef MA_HAS_AUDIO4
43078 case ma_backend_audio4:
43079 {
43080 pContext->callbacks.onContextInit = ma_context_init__audio4;
43081 } break;
43082 #endif
43083 #ifdef MA_HAS_OSS
43084 case ma_backend_oss:
43085 {
43086 pContext->callbacks.onContextInit = ma_context_init__oss;
43087 } break;
43088 #endif
43089 #ifdef MA_HAS_PULSEAUDIO
43091 {
43092 pContext->callbacks.onContextInit = ma_context_init__pulse;
43093 } break;
43094 #endif
43095 #ifdef MA_HAS_ALSA
43096 case ma_backend_alsa:
43097 {
43098 pContext->callbacks.onContextInit = ma_context_init__alsa;
43099 } break;
43100 #endif
43101 #ifdef MA_HAS_JACK
43102 case ma_backend_jack:
43103 {
43104 pContext->callbacks.onContextInit = ma_context_init__jack;
43105 } break;
43106 #endif
43107 #ifdef MA_HAS_AAUDIO
43108 case ma_backend_aaudio:
43109 {
43110 if (ma_is_backend_enabled(backend)) {
43111 pContext->callbacks.onContextInit = ma_context_init__aaudio;
43112 }
43113 } break;
43114 #endif
43115 #ifdef MA_HAS_OPENSL
43116 case ma_backend_opensl:
43117 {
43118 if (ma_is_backend_enabled(backend)) {
43119 pContext->callbacks.onContextInit = ma_context_init__opensl;
43120 }
43121 } break;
43122 #endif
43123 #ifdef MA_HAS_WEBAUDIO
43125 {
43126 pContext->callbacks.onContextInit = ma_context_init__webaudio;
43127 } break;
43128 #endif
43129 #ifdef MA_HAS_CUSTOM
43130 case ma_backend_custom:
43131 {
43132 /* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */
43133 pContext->callbacks = pConfig->custom;
43134 } break;
43135 #endif
43136 #ifdef MA_HAS_NULL
43137 case ma_backend_null:
43138 {
43139 pContext->callbacks.onContextInit = ma_context_init__null;
43140 } break;
43141 #endif
43142
43143 default: break;
43144 }
43145
43146 if (pContext->callbacks.onContextInit != NULL) {
43147 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend));
43148 result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks);
43149 } else {
43150 /* Getting here means the onContextInit callback is not set which means the backend is not enabled. Special case for the custom backend. */
43151 if (backend != ma_backend_custom) {
43152 result = MA_BACKEND_NOT_ENABLED;
43153 } else {
43154 #if !defined(MA_HAS_CUSTOM)
43155 result = MA_BACKEND_NOT_ENABLED;
43156 #else
43157 result = MA_NO_BACKEND;
43158 #endif
43159 }
43160 }
43161
43162 /* If this iteration was successful, return. */
43163 if (result == MA_SUCCESS) {
43164 result = ma_mutex_init(&pContext->deviceEnumLock);
43165 if (result != MA_SUCCESS) {
43166 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");
43167 }
43168
43169 result = ma_mutex_init(&pContext->deviceInfoLock);
43170 if (result != MA_SUCCESS) {
43171 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");
43172 }
43173
43174 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n");
43175 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE");
43176 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO");
43177 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO");
43178 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO");
43179
43180 pContext->backend = backend;
43181 return result;
43182 } else {
43183 if (result == MA_BACKEND_NOT_ENABLED) {
43184 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "%s backend is disabled.\n", ma_get_backend_name(backend));
43185 } else {
43186 ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend));
43187 }
43188 }
43189 }
43190
43191 /* If we get here it means an error occurred. */
43192 MA_ZERO_OBJECT(pContext); /* Safety. */
43193 return MA_NO_BACKEND;
43194}
43195
43197{
43198 if (pContext == NULL) {
43199 return MA_INVALID_ARGS;
43200 }
43201
43202 if (pContext->callbacks.onContextUninit != NULL) {
43203 pContext->callbacks.onContextUninit(pContext);
43204 }
43205
43206 ma_mutex_uninit(&pContext->deviceEnumLock);
43207 ma_mutex_uninit(&pContext->deviceInfoLock);
43208 ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks);
43209 ma_context_uninit_backend_apis(pContext);
43210
43211 if (pContext->pLog == &pContext->log) {
43212 ma_log_uninit(&pContext->log);
43213 }
43214
43215 return MA_SUCCESS;
43216}
43217
43218MA_API size_t ma_context_sizeof(void)
43219{
43220 return sizeof(ma_context);
43221}
43222
43223
43225{
43226 if (pContext == NULL) {
43227 return NULL;
43228 }
43229
43230 return pContext->pLog;
43231}
43232
43233
43235{
43236 ma_result result;
43237
43238 if (pContext == NULL || callback == NULL) {
43239 return MA_INVALID_ARGS;
43240 }
43241
43242 if (pContext->callbacks.onContextEnumerateDevices == NULL) {
43243 return MA_INVALID_OPERATION;
43244 }
43245
43246 ma_mutex_lock(&pContext->deviceEnumLock);
43247 {
43248 result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData);
43249 }
43250 ma_mutex_unlock(&pContext->deviceEnumLock);
43251
43252 return result;
43253}
43254
43255
43256static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
43257{
43258 /*
43259 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
43260 it's just appended to the end. If it's a playback device it's inserted just before the first capture device.
43261 */
43262
43263 /*
43264 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
43265 simple fixed size increment for buffer expansion.
43266 */
43267 const ma_uint32 bufferExpansionCount = 2;
43268 const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount;
43269
43270 if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) {
43271 ma_uint32 newCapacity = pContext->deviceInfoCapacity + bufferExpansionCount;
43272 ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, &pContext->allocationCallbacks);
43273 if (pNewInfos == NULL) {
43274 return MA_FALSE; /* Out of memory. */
43275 }
43276
43277 pContext->pDeviceInfos = pNewInfos;
43278 pContext->deviceInfoCapacity = newCapacity;
43279 }
43280
43281 if (deviceType == ma_device_type_playback) {
43282 /* Playback. Insert just before the first capture device. */
43283
43284 /* The first thing to do is move all of the capture devices down a slot. */
43285 ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount;
43286 size_t iCaptureDevice;
43287 for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) {
43288 pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1];
43289 }
43290
43291 /* Now just insert where the first capture device was before moving it down a slot. */
43292 pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo;
43293 pContext->playbackDeviceInfoCount += 1;
43294 } else {
43295 /* Capture. Insert at the end. */
43296 pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo;
43297 pContext->captureDeviceInfoCount += 1;
43298 }
43299
43300 (void)pUserData;
43301 return MA_TRUE;
43302}
43303
43304MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount)
43305{
43306 ma_result result;
43307
43308 /* Safety. */
43309 if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL;
43310 if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0;
43311 if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL;
43312 if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0;
43313
43314 if (pContext == NULL) {
43315 return MA_INVALID_ARGS;
43316 }
43317
43318 if (pContext->callbacks.onContextEnumerateDevices == NULL) {
43319 return MA_INVALID_OPERATION;
43320 }
43321
43322 /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */
43323 ma_mutex_lock(&pContext->deviceEnumLock);
43324 {
43325 /* Reset everything first. */
43326 pContext->playbackDeviceInfoCount = 0;
43327 pContext->captureDeviceInfoCount = 0;
43328
43329 /* Now enumerate over available devices. */
43330 result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL);
43331 if (result == MA_SUCCESS) {
43332 /* Playback devices. */
43333 if (ppPlaybackDeviceInfos != NULL) {
43334 *ppPlaybackDeviceInfos = pContext->pDeviceInfos;
43335 }
43336 if (pPlaybackDeviceCount != NULL) {
43337 *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount;
43338 }
43339
43340 /* Capture devices. */
43341 if (ppCaptureDeviceInfos != NULL) {
43342 *ppCaptureDeviceInfos = pContext->pDeviceInfos;
43343 /* Capture devices come after playback devices. */
43344 if (pContext->playbackDeviceInfoCount > 0) {
43345 /* Conditional, because NULL+0 is undefined behavior. */
43346 *ppCaptureDeviceInfos += pContext->playbackDeviceInfoCount;
43347 }
43348 }
43349 if (pCaptureDeviceCount != NULL) {
43350 *pCaptureDeviceCount = pContext->captureDeviceInfoCount;
43351 }
43352 }
43353 }
43354 ma_mutex_unlock(&pContext->deviceEnumLock);
43355
43356 return result;
43357}
43358
43359MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
43360{
43361 ma_result result;
43362 ma_device_info deviceInfo;
43363
43364 /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */
43365 if (pContext == NULL || pDeviceInfo == NULL) {
43366 return MA_INVALID_ARGS;
43367 }
43368
43369 MA_ZERO_OBJECT(&deviceInfo);
43370
43371 /* Help the backend out by copying over the device ID if we have one. */
43372 if (pDeviceID != NULL) {
43373 MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID));
43374 }
43375
43376 if (pContext->callbacks.onContextGetDeviceInfo == NULL) {
43377 return MA_INVALID_OPERATION;
43378 }
43379
43380 ma_mutex_lock(&pContext->deviceInfoLock);
43381 {
43382 result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo);
43383 }
43384 ma_mutex_unlock(&pContext->deviceInfoLock);
43385
43386 *pDeviceInfo = deviceInfo;
43387 return result;
43388}
43389
43391{
43392 if (pContext == NULL) {
43393 return MA_FALSE;
43394 }
43395
43396 return ma_is_loopback_supported(pContext->backend);
43397}
43398
43399
43401{
43402 ma_device_config config;
43403 MA_ZERO_OBJECT(&config);
43404 config.deviceType = deviceType;
43405 config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate don't matter here. */
43406
43407 return config;
43408}
43409
43410MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
43411{
43412 ma_result result;
43413 ma_device_descriptor descriptorPlayback;
43414 ma_device_descriptor descriptorCapture;
43415
43416 /* The context can be null, in which case we self-manage it. */
43417 if (pContext == NULL) {
43418 return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice);
43419 }
43420
43421 if (pDevice == NULL) {
43422 return MA_INVALID_ARGS;
43423 }
43424
43425 MA_ZERO_OBJECT(pDevice);
43426
43427 if (pConfig == NULL) {
43428 return MA_INVALID_ARGS;
43429 }
43430
43431 /* Check that we have our callbacks defined. */
43432 if (pContext->callbacks.onDeviceInit == NULL) {
43433 return MA_INVALID_OPERATION;
43434 }
43435
43436 /* Basic config validation. */
43437 if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
43438 if (pConfig->capture.channels > MA_MAX_CHANNELS) {
43439 return MA_INVALID_ARGS;
43440 }
43441
43442 if (!ma__is_channel_map_valid(pConfig->capture.pChannelMap, pConfig->capture.channels)) {
43443 return MA_INVALID_ARGS;
43444 }
43445 }
43446
43448 if (pConfig->playback.channels > MA_MAX_CHANNELS) {
43449 return MA_INVALID_ARGS;
43450 }
43451
43452 if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) {
43453 return MA_INVALID_ARGS;
43454 }
43455 }
43456
43457 pDevice->pContext = pContext;
43458
43459 /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */
43460 pDevice->pUserData = pConfig->pUserData;
43461 pDevice->onData = pConfig->dataCallback;
43462 pDevice->onNotification = pConfig->notificationCallback;
43463 pDevice->onStop = pConfig->stopCallback;
43464
43465 if (pConfig->playback.pDeviceID != NULL) {
43466 MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id));
43467 pDevice->playback.pID = &pDevice->playback.id;
43468 } else {
43469 pDevice->playback.pID = NULL;
43470 }
43471
43472 if (pConfig->capture.pDeviceID != NULL) {
43473 MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id));
43474 pDevice->capture.pID = &pDevice->capture.id;
43475 } else {
43476 pDevice->capture.pID = NULL;
43477 }
43478
43480 pDevice->noClip = pConfig->noClip;
43481 pDevice->noDisableDenormals = pConfig->noDisableDenormals;
43482 pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback;
43483 ma_atomic_float_set(&pDevice->masterVolumeFactor, 1);
43484
43485 pDevice->type = pConfig->deviceType;
43486 pDevice->sampleRate = pConfig->sampleRate;
43487 pDevice->resampling.algorithm = pConfig->resampling.algorithm;
43488 pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder;
43491
43492 pDevice->capture.shareMode = pConfig->capture.shareMode;
43493 pDevice->capture.format = pConfig->capture.format;
43494 pDevice->capture.channels = pConfig->capture.channels;
43495 ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels);
43496 pDevice->capture.channelMixMode = pConfig->capture.channelMixMode;
43498
43499 pDevice->playback.shareMode = pConfig->playback.shareMode;
43500 pDevice->playback.format = pConfig->playback.format;
43501 pDevice->playback.channels = pConfig->playback.channels;
43503 pDevice->playback.channelMixMode = pConfig->playback.channelMixMode;
43505
43506 result = ma_mutex_init(&pDevice->startStopLock);
43507 if (result != MA_SUCCESS) {
43508 return result;
43509 }
43510
43511 /*
43512 When the device is started, the worker thread is the one that does the actual startup of the backend device. We
43513 use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device.
43514
43515 Each of these semaphores is released internally by the worker thread when the work is completed. The start
43516 semaphore is also used to wake up the worker thread.
43517 */
43518 result = ma_event_init(&pDevice->wakeupEvent);
43519 if (result != MA_SUCCESS) {
43520 ma_mutex_uninit(&pDevice->startStopLock);
43521 return result;
43522 }
43523
43524 result = ma_event_init(&pDevice->startEvent);
43525 if (result != MA_SUCCESS) {
43526 ma_event_uninit(&pDevice->wakeupEvent);
43527 ma_mutex_uninit(&pDevice->startStopLock);
43528 return result;
43529 }
43530
43531 result = ma_event_init(&pDevice->stopEvent);
43532 if (result != MA_SUCCESS) {
43533 ma_event_uninit(&pDevice->startEvent);
43534 ma_event_uninit(&pDevice->wakeupEvent);
43535 ma_mutex_uninit(&pDevice->startStopLock);
43536 return result;
43537 }
43538
43539
43540 MA_ZERO_OBJECT(&descriptorPlayback);
43541 descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID;
43542 descriptorPlayback.shareMode = pConfig->playback.shareMode;
43543 descriptorPlayback.format = pConfig->playback.format;
43544 descriptorPlayback.channels = pConfig->playback.channels;
43545 descriptorPlayback.sampleRate = pConfig->sampleRate;
43546 ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels);
43547 descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames;
43548 descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
43549 descriptorPlayback.periodCount = pConfig->periods;
43550
43551 if (descriptorPlayback.periodCount == 0) {
43552 descriptorPlayback.periodCount = MA_DEFAULT_PERIODS;
43553 }
43554
43555
43556 MA_ZERO_OBJECT(&descriptorCapture);
43557 descriptorCapture.pDeviceID = pConfig->capture.pDeviceID;
43558 descriptorCapture.shareMode = pConfig->capture.shareMode;
43559 descriptorCapture.format = pConfig->capture.format;
43560 descriptorCapture.channels = pConfig->capture.channels;
43561 descriptorCapture.sampleRate = pConfig->sampleRate;
43562 ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels);
43563 descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames;
43564 descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
43565 descriptorCapture.periodCount = pConfig->periods;
43566
43567 if (descriptorCapture.periodCount == 0) {
43568 descriptorCapture.periodCount = MA_DEFAULT_PERIODS;
43569 }
43570
43571
43572 result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture);
43573 if (result != MA_SUCCESS) {
43574 ma_event_uninit(&pDevice->startEvent);
43575 ma_event_uninit(&pDevice->wakeupEvent);
43576 ma_mutex_uninit(&pDevice->startStopLock);
43577 return result;
43578 }
43579
43580#if 0
43581 /*
43582 On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between
43583 the requested format and the internal format.
43584 */
43586 if (!ma_device_descriptor_is_valid(&descriptorCapture)) {
43587 ma_device_uninit(pDevice);
43588 return MA_INVALID_ARGS;
43589 }
43590
43591 pDevice->capture.internalFormat = descriptorCapture.format;
43592 pDevice->capture.internalChannels = descriptorCapture.channels;
43593 pDevice->capture.internalSampleRate = descriptorCapture.sampleRate;
43594 ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels);
43595 pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames;
43596 pDevice->capture.internalPeriods = descriptorCapture.periodCount;
43597
43598 if (pDevice->capture.internalPeriodSizeInFrames == 0) {
43600 }
43601 }
43602
43603 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
43604 if (!ma_device_descriptor_is_valid(&descriptorPlayback)) {
43605 ma_device_uninit(pDevice);
43606 return MA_INVALID_ARGS;
43607 }
43608
43609 pDevice->playback.internalFormat = descriptorPlayback.format;
43610 pDevice->playback.internalChannels = descriptorPlayback.channels;
43611 pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate;
43612 ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels);
43613 pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames;
43614 pDevice->playback.internalPeriods = descriptorPlayback.periodCount;
43615
43616 if (pDevice->playback.internalPeriodSizeInFrames == 0) {
43618 }
43619 }
43620
43621
43622 /*
43623 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.
43624 For loopback devices, we need to retrieve the name of the playback device.
43625 */
43626 {
43627 ma_device_info deviceInfo;
43628
43631 if (result == MA_SUCCESS) {
43632 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);
43633 } else {
43634 /* We failed to retrieve the device info. Fall back to a default name. */
43635 if (descriptorCapture.pDeviceID == NULL) {
43636 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
43637 } else {
43638 ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1);
43639 }
43640 }
43641 }
43642
43643 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
43644 result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo);
43645 if (result == MA_SUCCESS) {
43646 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1);
43647 } else {
43648 /* We failed to retrieve the device info. Fall back to a default name. */
43649 if (descriptorPlayback.pDeviceID == NULL) {
43650 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
43651 } else {
43652 ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1);
43653 }
43654 }
43655 }
43656 }
43657
43658
43659 ma_device__post_init_setup(pDevice, pConfig->deviceType);
43660#endif
43661
43662 result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture);
43663 if (result != MA_SUCCESS) {
43664 ma_device_uninit(pDevice);
43665 return result;
43666 }
43667
43668
43669 /*
43670 If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to
43671 be done after post_init_setup() because we'll need access to the sample rate.
43672 */
43673 if (pConfig->noFixedSizedCallback == MA_FALSE) {
43674 /* We're using a fixed sized data callback so we'll need an intermediary buffer. */
43675 ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames;
43676 if (intermediaryBufferCap == 0) {
43678 }
43679
43681 ma_uint32 intermediaryBufferSizeInBytes;
43682
43683 pDevice->capture.intermediaryBufferLen = 0;
43684 pDevice->capture.intermediaryBufferCap = intermediaryBufferCap;
43685 if (pDevice->capture.intermediaryBufferCap == 0) {
43687 }
43688
43689 intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
43690
43691 pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks);
43692 if (pDevice->capture.pIntermediaryBuffer == NULL) {
43693 ma_device_uninit(pDevice);
43694 return MA_OUT_OF_MEMORY;
43695 }
43696
43697 /* Silence the buffer for safety. */
43700 }
43701
43702 if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
43703 ma_uint64 intermediaryBufferSizeInBytes;
43704
43705 pDevice->playback.intermediaryBufferLen = 0;
43706 if (pConfig->deviceType == ma_device_type_duplex) {
43707 pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap; /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */
43708 } else {
43709 pDevice->playback.intermediaryBufferCap = intermediaryBufferCap;
43710 if (pDevice->playback.intermediaryBufferCap == 0) {
43712 }
43713 }
43714
43715 intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
43716
43717 pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks);
43718 if (pDevice->playback.pIntermediaryBuffer == NULL) {
43719 ma_device_uninit(pDevice);
43720 return MA_OUT_OF_MEMORY;
43721 }
43722
43723 /* Silence the buffer for safety. */
43725 pDevice->playback.intermediaryBufferLen = 0;
43726 }
43727 } else {
43728 /* Not using a fixed sized data callback so no need for an intermediary buffer. */
43729 }
43730
43731
43732 /* Some backends don't require the worker thread. */
43733 if (!ma_context_is_backend_asynchronous(pContext)) {
43734 /* The worker thread. */
43735 result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks);
43736 if (result != MA_SUCCESS) {
43737 ma_device_uninit(pDevice);
43738 return result;
43739 }
43740
43741 /* Wait for the worker thread to put the device into its stopped state for real. */
43742 ma_event_wait(&pDevice->stopEvent);
43743 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
43744 } else {
43745 /*
43746 If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done
43747 after ma_device__post_init_setup().
43748 */
43749 if (ma_context_is_backend_asynchronous(pContext)) {
43750 if (pConfig->deviceType == ma_device_type_duplex) {
43751 result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);
43752 if (result != MA_SUCCESS) {
43753 ma_device_uninit(pDevice);
43754 return result;
43755 }
43756 }
43757 }
43758
43759 ma_device__set_state(pDevice, ma_device_state_stopped);
43760 }
43761
43762 /* Log device information. */
43763 {
43765 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
43766 char name[MA_MAX_DEVICE_NAME_LENGTH + 1];
43767 ma_device_get_name(pDevice, ma_device_type_capture, name, sizeof(name), NULL);
43768
43769 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture");
43771 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels);
43772 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate);
43774 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n");
43775 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO");
43776 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO");
43777 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO");
43778 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO");
43779 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO");
43780 {
43781 char channelMapStr[1024];
43782 ma_channel_map_to_string(pDevice->capture.internalChannelMap, pDevice->capture.internalChannels, channelMapStr, sizeof(channelMapStr));
43783 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr);
43784
43785 ma_channel_map_to_string(pDevice->capture.channelMap, pDevice->capture.channels, channelMapStr, sizeof(channelMapStr));
43786 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr);
43787 }
43788 }
43789 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
43790 char name[MA_MAX_DEVICE_NAME_LENGTH + 1];
43791 ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL);
43792
43793 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Playback");
43795 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels);
43796 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate);
43798 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n");
43799 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO");
43800 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO");
43801 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO");
43802 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO");
43803 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO");
43804 {
43805 char channelMapStr[1024];
43806 ma_channel_map_to_string(pDevice->playback.channelMap, pDevice->playback.channels, channelMapStr, sizeof(channelMapStr));
43807 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr);
43808
43809 ma_channel_map_to_string(pDevice->playback.internalChannelMap, pDevice->playback.internalChannels, channelMapStr, sizeof(channelMapStr));
43810 ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr);
43811 }
43812 }
43813 }
43814
43815 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
43816 return MA_SUCCESS;
43817}
43818
43819MA_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)
43820{
43821 ma_result result;
43822 ma_context* pContext;
43823 ma_backend defaultBackends[ma_backend_null+1];
43824 ma_uint32 iBackend;
43825 ma_backend* pBackendsToIterate;
43826 ma_uint32 backendsToIterateCount;
43827 ma_allocation_callbacks allocationCallbacks;
43828
43829 if (pConfig == NULL) {
43830 return MA_INVALID_ARGS;
43831 }
43832
43833 if (pContextConfig != NULL) {
43834 result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks);
43835 if (result != MA_SUCCESS) {
43836 return result;
43837 }
43838 } else {
43839 allocationCallbacks = ma_allocation_callbacks_init_default();
43840 }
43841
43842 pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks);
43843 if (pContext == NULL) {
43844 return MA_OUT_OF_MEMORY;
43845 }
43846
43847 for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
43848 defaultBackends[iBackend] = (ma_backend)iBackend;
43849 }
43850
43851 pBackendsToIterate = (ma_backend*)backends;
43852 backendsToIterateCount = backendCount;
43853 if (pBackendsToIterate == NULL) {
43854 pBackendsToIterate = (ma_backend*)defaultBackends;
43855 backendsToIterateCount = ma_countof(defaultBackends);
43856 }
43857
43858 result = MA_NO_BACKEND;
43859
43860 for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
43861 /*
43862 This is a hack for iOS. If the context config is null, there's a good chance the
43863 `ma_device_init(NULL, &deviceConfig, pDevice);` pattern is being used. In this
43864 case, set the session category based on the device type.
43865 */
43866 #if defined(MA_APPLE_MOBILE)
43867 ma_context_config contextConfig;
43868
43869 if (pContextConfig == NULL) {
43870 contextConfig = ma_context_config_init();
43871 switch (pConfig->deviceType) {
43872 case ma_device_type_duplex: {
43874 } break;
43877 } break;
43879 default: {
43881 } break;
43882 }
43883
43884 pContextConfig = &contextConfig;
43885 }
43886 #endif
43887
43888 result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext);
43889 if (result == MA_SUCCESS) {
43890 result = ma_device_init(pContext, pConfig, pDevice);
43891 if (result == MA_SUCCESS) {
43892 break; /* Success. */
43893 } else {
43894 ma_context_uninit(pContext); /* Failure. */
43895 }
43896 }
43897 }
43898
43899 if (result != MA_SUCCESS) {
43900 ma_free(pContext, &allocationCallbacks);
43901 return result;
43902 }
43903
43904 pDevice->isOwnerOfContext = MA_TRUE;
43905 return result;
43906}
43907
43908MA_API void ma_device_uninit(ma_device* pDevice)
43909{
43910 if (!ma_device__is_initialized(pDevice)) {
43911 return;
43912 }
43913
43914 /*
43915 It's possible for the miniaudio side of the device and the backend to not be in sync due to
43916 system-level situations such as the computer being put into sleep mode and the backend not
43917 notifying miniaudio of the fact the device has stopped. It's possible for this to result in a
43918 deadlock due to miniaudio thinking the device is in a running state, when in fact it's not
43919 running at all. For this reason I am no longer explicitly stopping the device. I don't think
43920 this should affect anyone in practice since uninitializing the backend will naturally stop the
43921 device anyway.
43922 */
43923 #if 0
43924 {
43925 /* 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. */
43926 if (ma_device_is_started(pDevice)) {
43927 ma_device_stop(pDevice);
43928 }
43929 }
43930 #endif
43931
43932 /* Putting the device into an uninitialized state will make the worker thread return. */
43933 ma_device__set_state(pDevice, ma_device_state_uninitialized);
43934
43935 /* Wake up the worker thread and wait for it to properly terminate. */
43936 if (!ma_context_is_backend_asynchronous(pDevice->pContext)) {
43937 ma_event_signal(&pDevice->wakeupEvent);
43938 ma_thread_wait(&pDevice->thread);
43939 }
43940
43941 if (pDevice->pContext->callbacks.onDeviceUninit != NULL) {
43942 pDevice->pContext->callbacks.onDeviceUninit(pDevice);
43943 }
43944
43945
43946 ma_event_uninit(&pDevice->stopEvent);
43947 ma_event_uninit(&pDevice->startEvent);
43948 ma_event_uninit(&pDevice->wakeupEvent);
43949 ma_mutex_uninit(&pDevice->startStopLock);
43950
43951 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
43952 if (pDevice->type == ma_device_type_duplex) {
43953 ma_duplex_rb_uninit(&pDevice->duplexRB);
43954 }
43955 }
43956
43957 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
43959 }
43960 if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
43962 }
43963
43964 if (pDevice->playback.pInputCache != NULL) {
43966 }
43967
43968 if (pDevice->capture.pIntermediaryBuffer != NULL) {
43970 }
43971 if (pDevice->playback.pIntermediaryBuffer != NULL) {
43973 }
43974
43975 if (pDevice->isOwnerOfContext) {
43976 ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks;
43977
43978 ma_context_uninit(pDevice->pContext);
43979 ma_free(pDevice->pContext, &allocationCallbacks);
43980 }
43981
43982 MA_ZERO_OBJECT(pDevice);
43983}
43984
43986{
43987 if (pDevice == NULL) {
43988 return NULL;
43989 }
43990
43991 return pDevice->pContext;
43992}
43993
43995{
43997}
43998
44000{
44001 if (pDeviceInfo == NULL) {
44002 return MA_INVALID_ARGS;
44003 }
44004
44005 MA_ZERO_OBJECT(pDeviceInfo);
44006
44007 if (pDevice == NULL) {
44008 return MA_INVALID_ARGS;
44009 }
44010
44011 /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */
44012 if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) {
44013 return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo);
44014 }
44015
44016 /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */
44017 if (type == ma_device_type_playback) {
44018 return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo);
44019 } else {
44020 /*
44021 Here we're getting the capture side, which is the branch we'll be entering for a loopback
44022 device, since loopback is capturing. However, if the device is using the default device ID,
44023 it won't get the correct information because it'll think we're asking for the default
44024 capture device, where in fact for loopback we want the default *playback* device. We'll do
44025 a bit of a hack here to make sure we get the correct info.
44026 */
44027 if (pDevice->type == ma_device_type_loopback && pDevice->capture.pID == NULL) {
44029 }
44030
44031 return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo);
44032 }
44033}
44034
44035MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator)
44036{
44037 ma_result result;
44038 ma_device_info deviceInfo;
44039
44040 if (pLengthNotIncludingNullTerminator != NULL) {
44041 *pLengthNotIncludingNullTerminator = 0;
44042 }
44043
44044 if (pName != NULL && nameCap > 0) {
44045 pName[0] = '\0';
44046 }
44047
44048 result = ma_device_get_info(pDevice, type, &deviceInfo);
44049 if (result != MA_SUCCESS) {
44050 return result;
44051 }
44052
44053 if (pName != NULL) {
44054 ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1);
44055
44056 /*
44057 For safety, make sure the length is based on the truncated output string rather than the
44058 source. Otherwise the caller might assume the output buffer contains more content than it
44059 actually does.
44060 */
44061 if (pLengthNotIncludingNullTerminator != NULL) {
44062 *pLengthNotIncludingNullTerminator = strlen(pName);
44063 }
44064 } else {
44065 /* Name not specified. Just report the length of the source string. */
44066 if (pLengthNotIncludingNullTerminator != NULL) {
44067 *pLengthNotIncludingNullTerminator = strlen(deviceInfo.name);
44068 }
44069 }
44070
44071 return MA_SUCCESS;
44072}
44073
44075{
44076 ma_result result;
44077
44078 if (pDevice == NULL) {
44079 return MA_INVALID_ARGS;
44080 }
44081
44083 return MA_INVALID_OPERATION; /* Not initialized. */
44084 }
44085
44087 return MA_SUCCESS; /* Already started. */
44088 }
44089
44090 ma_mutex_lock(&pDevice->startStopLock);
44091 {
44092 /*
44093 We need to check again if the device is in a started state because it's possible for one thread to have started the device
44094 while another was waiting on the mutex.
44095 */
44097 ma_mutex_unlock(&pDevice->startStopLock);
44098 return MA_SUCCESS; /* Already started. */
44099 }
44100
44101 /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */
44102 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
44103
44104 ma_device__set_state(pDevice, ma_device_state_starting);
44105
44106 /* Asynchronous backends need to be handled differently. */
44107 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
44108 if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
44109 result = pDevice->pContext->callbacks.onDeviceStart(pDevice);
44110 } else {
44111 result = MA_INVALID_OPERATION;
44112 }
44113
44114 if (result == MA_SUCCESS) {
44115 ma_device__set_state(pDevice, ma_device_state_started);
44116 ma_device__on_notification_started(pDevice);
44117 }
44118 } else {
44119 /*
44120 Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the
44121 thread and then wait for the start event.
44122 */
44123 ma_event_signal(&pDevice->wakeupEvent);
44124
44125 /*
44126 Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device
44127 into the started state. Don't call ma_device__set_state() here.
44128 */
44129 ma_event_wait(&pDevice->startEvent);
44130 result = pDevice->workResult;
44131 }
44132
44133 /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */
44134 if (result != MA_SUCCESS) {
44135 ma_device__set_state(pDevice, ma_device_state_stopped);
44136 }
44137 }
44138 ma_mutex_unlock(&pDevice->startStopLock);
44139
44140 return result;
44141}
44142
44144{
44145 ma_result result;
44146
44147 if (pDevice == NULL) {
44148 return MA_INVALID_ARGS;
44149 }
44150
44152 return MA_INVALID_OPERATION; /* Not initialized. */
44153 }
44154
44156 return MA_SUCCESS; /* Already stopped. */
44157 }
44158
44159 ma_mutex_lock(&pDevice->startStopLock);
44160 {
44161 /*
44162 We need to check again if the device is in a stopped state because it's possible for one thread to have stopped the device
44163 while another was waiting on the mutex.
44164 */
44166 ma_mutex_unlock(&pDevice->startStopLock);
44167 return MA_SUCCESS; /* Already stopped. */
44168 }
44169
44170 /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */
44171 MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started);
44172
44173 ma_device__set_state(pDevice, ma_device_state_stopping);
44174
44175 /* Asynchronous backends need to be handled differently. */
44176 if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
44177 /* Asynchronous backends must have a stop operation. */
44178 if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
44179 result = pDevice->pContext->callbacks.onDeviceStop(pDevice);
44180 } else {
44181 result = MA_INVALID_OPERATION;
44182 }
44183
44184 ma_device__set_state(pDevice, ma_device_state_stopped);
44185 } else {
44186 /*
44187 Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If
44188 the backend is implementing its own audio thread loop we'll need to wake it up if required. Note that we need to make
44189 sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super
44190 important though, so I'm asserting it here as well for extra safety in case we accidentally change something later.
44191 */
44192 MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started);
44193
44194 if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) {
44195 pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice);
44196 }
44197
44198 /*
44199 We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be
44200 the one who puts the device into the stopped state. Don't call ma_device__set_state() here.
44201 */
44202 ma_event_wait(&pDevice->stopEvent);
44203 result = MA_SUCCESS;
44204 }
44205
44206 /*
44207 This is a safety measure to ensure the internal buffer has been cleared so any leftover
44208 does not get played the next time the device starts. Ideally this should be drained by
44209 the backend first.
44210 */
44211 pDevice->playback.intermediaryBufferLen = 0;
44212 pDevice->playback.inputCacheConsumed = 0;
44213 pDevice->playback.inputCacheRemaining = 0;
44214 }
44215 ma_mutex_unlock(&pDevice->startStopLock);
44216
44217 return result;
44218}
44219
44221{
44223}
44224
44226{
44227 if (pDevice == NULL) {
44229 }
44230
44231 return ma_atomic_device_state_get((ma_atomic_device_state*)&pDevice->state); /* Naughty cast to get rid of a const warning. */
44232}
44233
44235{
44236 if (pDevice == NULL) {
44237 return MA_INVALID_ARGS;
44238 }
44239
44240 if (volume < 0.0f) {
44241 return MA_INVALID_ARGS;
44242 }
44243
44244 ma_atomic_float_set(&pDevice->masterVolumeFactor, volume);
44245
44246 return MA_SUCCESS;
44247}
44248
44249MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume)
44250{
44251 if (pVolume == NULL) {
44252 return MA_INVALID_ARGS;
44253 }
44254
44255 if (pDevice == NULL) {
44256 *pVolume = 0;
44257 return MA_INVALID_ARGS;
44258 }
44259
44260 *pVolume = ma_atomic_float_get(&pDevice->masterVolumeFactor);
44261
44262 return MA_SUCCESS;
44263}
44264
44266{
44267 if (gainDB > 0) {
44268 return MA_INVALID_ARGS;
44269 }
44270
44271 return ma_device_set_master_volume(pDevice, ma_volume_db_to_linear(gainDB));
44272}
44273
44275{
44276 float factor;
44277 ma_result result;
44278
44279 if (pGainDB == NULL) {
44280 return MA_INVALID_ARGS;
44281 }
44282
44283 result = ma_device_get_master_volume(pDevice, &factor);
44284 if (result != MA_SUCCESS) {
44285 *pGainDB = 0;
44286 return result;
44287 }
44288
44289 *pGainDB = ma_volume_linear_to_db(factor);
44290
44291 return MA_SUCCESS;
44292}
44293
44294
44295MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
44296{
44297 if (pDevice == NULL) {
44298 return MA_INVALID_ARGS;
44299 }
44300
44301 if (pOutput == NULL && pInput == NULL) {
44302 return MA_INVALID_ARGS;
44303 }
44304
44305 /*
44306 There is an assert deeper in the code that checks that frameCount > 0. Since this is a public facing
44307 API we'll need to check for that here. I've had reports that AAudio can sometimes post a frame count
44308 of 0.
44309 */
44310 if (frameCount == 0) {
44311 return MA_INVALID_ARGS;
44312 }
44313
44314 if (pDevice->type == ma_device_type_duplex) {
44315 if (pInput != NULL) {
44316 ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb);
44317 }
44318
44319 if (pOutput != NULL) {
44320 ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb);
44321 }
44322 } else {
44323 if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) {
44324 if (pInput == NULL) {
44325 return MA_INVALID_ARGS;
44326 }
44327
44328 ma_device__send_frames_to_client(pDevice, frameCount, pInput);
44329 }
44330
44331 if (pDevice->type == ma_device_type_playback) {
44332 if (pOutput == NULL) {
44333 return MA_INVALID_ARGS;
44334 }
44335
44336 ma_device__read_frames_from_client(pDevice, frameCount, pOutput);
44337 }
44338 }
44339
44340 return MA_SUCCESS;
44341}
44342
44344{
44345 if (pDescriptor == NULL) {
44346 return 0;
44347 }
44348
44349 /*
44350 We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the
44351 time when the size of the buffer needs to be determined. In this case we need to just take a best
44352 guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll
44353 just fall back to MA_DEFAULT_SAMPLE_RATE.
44354 */
44355 if (nativeSampleRate == 0) {
44356 nativeSampleRate = pDescriptor->sampleRate;
44357 }
44358 if (nativeSampleRate == 0) {
44359 nativeSampleRate = MA_DEFAULT_SAMPLE_RATE;
44360 }
44361
44362 MA_ASSERT(nativeSampleRate != 0);
44363
44364 if (pDescriptor->periodSizeInFrames == 0) {
44365 if (pDescriptor->periodSizeInMilliseconds == 0) {
44366 if (performanceProfile == ma_performance_profile_low_latency) {
44367 return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate);
44368 } else {
44369 return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate);
44370 }
44371 } else {
44373 }
44374 } else {
44375 return pDescriptor->periodSizeInFrames;
44376 }
44377}
44378#endif /* MA_NO_DEVICE_IO */
44379
44380
44382{
44383 /* Prevent a division by zero. */
44384 if (sampleRate == 0) {
44385 return 0;
44386 }
44387
44388 return (bufferSizeInFrames*1000 + (sampleRate - 1)) / sampleRate;
44389}
44390
44392{
44393 /* Prevent a division by zero. */
44394 if (sampleRate == 0) {
44395 return 0;
44396 }
44397
44398 return bufferSizeInMilliseconds*sampleRate / 1000;
44399}
44400
44401MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
44402{
44403 if (dst == src) {
44404 return; /* No-op. */
44405 }
44406
44407 ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels));
44408}
44409
44410MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
44411{
44412 if (format == ma_format_u8) {
44413 ma_uint64 sampleCount = frameCount * channels;
44414 ma_uint64 iSample;
44415 for (iSample = 0; iSample < sampleCount; iSample += 1) {
44416 ((ma_uint8*)p)[iSample] = 128;
44417 }
44418 } else {
44419 ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels));
44420 }
44421}
44422
44423MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
44424{
44425 return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels));
44426}
44427
44428MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
44429{
44430 return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels));
44431}
44432
44433
44434MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count)
44435{
44436 ma_uint64 iSample;
44437
44438 MA_ASSERT(pDst != NULL);
44439 MA_ASSERT(pSrc != NULL);
44440
44441 for (iSample = 0; iSample < count; iSample += 1) {
44442 pDst[iSample] = ma_clip_u8(pSrc[iSample]);
44443 }
44444}
44445
44446MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count)
44447{
44448 ma_uint64 iSample;
44449
44450 MA_ASSERT(pDst != NULL);
44451 MA_ASSERT(pSrc != NULL);
44452
44453 for (iSample = 0; iSample < count; iSample += 1) {
44454 pDst[iSample] = ma_clip_s16(pSrc[iSample]);
44455 }
44456}
44457
44458MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count)
44459{
44460 ma_uint64 iSample;
44461
44462 MA_ASSERT(pDst != NULL);
44463 MA_ASSERT(pSrc != NULL);
44464
44465 for (iSample = 0; iSample < count; iSample += 1) {
44466 ma_int64 s = ma_clip_s24(pSrc[iSample]);
44467 pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0);
44468 pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8);
44469 pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);
44470 }
44471}
44472
44473MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count)
44474{
44475 ma_uint64 iSample;
44476
44477 MA_ASSERT(pDst != NULL);
44478 MA_ASSERT(pSrc != NULL);
44479
44480 for (iSample = 0; iSample < count; iSample += 1) {
44481 pDst[iSample] = ma_clip_s32(pSrc[iSample]);
44482 }
44483}
44484
44485MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count)
44486{
44487 ma_uint64 iSample;
44488
44489 MA_ASSERT(pDst != NULL);
44490 MA_ASSERT(pSrc != NULL);
44491
44492 for (iSample = 0; iSample < count; iSample += 1) {
44493 pDst[iSample] = ma_clip_f32(pSrc[iSample]);
44494 }
44495}
44496
44497MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
44498{
44499 ma_uint64 sampleCount;
44500
44501 MA_ASSERT(pDst != NULL);
44502 MA_ASSERT(pSrc != NULL);
44503
44504 sampleCount = frameCount * channels;
44505
44506 switch (format) {
44507 case ma_format_u8: ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break;
44508 case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break;
44509 case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break;
44510 case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break;
44511 case ma_format_f32: ma_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount); break;
44512
44513 /* 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. */
44514 case ma_format_unknown:
44515 case ma_format_count:
44516 break;
44517 }
44518}
44519
44520
44521MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor)
44522{
44523 ma_uint64 iSample;
44524
44525 if (pSamplesOut == NULL || pSamplesIn == NULL) {
44526 return;
44527 }
44528
44529 for (iSample = 0; iSample < sampleCount; iSample += 1) {
44530 pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor);
44531 }
44532}
44533
44534MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor)
44535{
44536 ma_uint64 iSample;
44537
44538 if (pSamplesOut == NULL || pSamplesIn == NULL) {
44539 return;
44540 }
44541
44542 for (iSample = 0; iSample < sampleCount; iSample += 1) {
44543 pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor);
44544 }
44545}
44546
44547MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor)
44548{
44549 ma_uint64 iSample;
44550 ma_uint8* pSamplesOut8;
44551 ma_uint8* pSamplesIn8;
44552
44553 if (pSamplesOut == NULL || pSamplesIn == NULL) {
44554 return;
44555 }
44556
44557 pSamplesOut8 = (ma_uint8*)pSamplesOut;
44558 pSamplesIn8 = (ma_uint8*)pSamplesIn;
44559
44560 for (iSample = 0; iSample < sampleCount; iSample += 1) {
44561 ma_int32 sampleS32;
44562
44563 sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24);
44564 sampleS32 = (ma_int32)(sampleS32 * factor);
44565
44566 pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8);
44567 pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16);
44568 pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24);
44569 }
44570}
44571
44572MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor)
44573{
44574 ma_uint64 iSample;
44575
44576 if (pSamplesOut == NULL || pSamplesIn == NULL) {
44577 return;
44578 }
44579
44580 for (iSample = 0; iSample < sampleCount; iSample += 1) {
44581 pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor);
44582 }
44583}
44584
44585MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor)
44586{
44587 ma_uint64 iSample;
44588
44589 if (pSamplesOut == NULL || pSamplesIn == NULL) {
44590 return;
44591 }
44592
44593 if (factor == 1) {
44594 if (pSamplesOut == pSamplesIn) {
44595 /* In place. No-op. */
44596 } else {
44597 /* Just a copy. */
44598 for (iSample = 0; iSample < sampleCount; iSample += 1) {
44599 pSamplesOut[iSample] = pSamplesIn[iSample];
44600 }
44601 }
44602 } else {
44603 for (iSample = 0; iSample < sampleCount; iSample += 1) {
44604 pSamplesOut[iSample] = pSamplesIn[iSample] * factor;
44605 }
44606 }
44607}
44608
44609MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor)
44610{
44611 ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor);
44612}
44613
44614MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor)
44615{
44616 ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor);
44617}
44618
44619MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor)
44620{
44621 ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor);
44622}
44623
44624MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor)
44625{
44626 ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor);
44627}
44628
44629MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor)
44630{
44631 ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor);
44632}
44633
44634MA_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)
44635{
44636 ma_copy_and_apply_volume_factor_u8(pFramesOut, pFramesIn, frameCount*channels, factor);
44637}
44638
44639MA_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)
44640{
44641 ma_copy_and_apply_volume_factor_s16(pFramesOut, pFramesIn, frameCount*channels, factor);
44642}
44643
44644MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
44645{
44646 ma_copy_and_apply_volume_factor_s24(pFramesOut, pFramesIn, frameCount*channels, factor);
44647}
44648
44649MA_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)
44650{
44651 ma_copy_and_apply_volume_factor_s32(pFramesOut, pFramesIn, frameCount*channels, factor);
44652}
44653
44654MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
44655{
44656 ma_copy_and_apply_volume_factor_f32(pFramesOut, pFramesIn, frameCount*channels, factor);
44657}
44658
44659MA_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)
44660{
44661 switch (format)
44662 {
44663 case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pFramesOut, (const ma_uint8*)pFramesIn, frameCount, channels, factor); return;
44664 case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pFramesOut, (const ma_int16*)pFramesIn, frameCount, channels, factor); return;
44665 case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pFramesOut, pFramesIn, frameCount, channels, factor); return;
44666 case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pFramesOut, (const ma_int32*)pFramesIn, frameCount, channels, factor); return;
44667 case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pFramesOut, (const float*)pFramesIn, frameCount, channels, factor); return;
44668 default: return; /* Do nothing. */
44669 }
44670}
44671
44672MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
44673{
44674 ma_copy_and_apply_volume_factor_pcm_frames_u8(pFrames, pFrames, frameCount, channels, factor);
44675}
44676
44677MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
44678{
44679 ma_copy_and_apply_volume_factor_pcm_frames_s16(pFrames, pFrames, frameCount, channels, factor);
44680}
44681
44682MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
44683{
44684 ma_copy_and_apply_volume_factor_pcm_frames_s24(pFrames, pFrames, frameCount, channels, factor);
44685}
44686
44687MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
44688{
44689 ma_copy_and_apply_volume_factor_pcm_frames_s32(pFrames, pFrames, frameCount, channels, factor);
44690}
44691
44692MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
44693{
44694 ma_copy_and_apply_volume_factor_pcm_frames_f32(pFrames, pFrames, frameCount, channels, factor);
44695}
44696
44697MA_API void ma_apply_volume_factor_pcm_frames(void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor)
44698{
44699 ma_copy_and_apply_volume_factor_pcm_frames(pFramesOut, pFramesOut, frameCount, format, channels, factor);
44700}
44701
44702
44703MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains)
44704{
44705 ma_uint64 iFrame;
44706
44707 if (channels == 2) {
44708 /* TODO: Do an optimized implementation for stereo and mono. Can do a SIMD optimized implementation as well. */
44709 }
44710
44711 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
44712 ma_uint32 iChannel;
44713 for (iChannel = 0; iChannel < channels; iChannel += 1) {
44714 pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel];
44715 }
44716 }
44717}
44718
44719
44720
44721static MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume)
44722{
44723 return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8);
44724}
44725
44726static MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume)
44727{
44728 return (ma_int32)((x * volume) >> 8);
44729}
44730
44731static MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume)
44732{
44733 return (ma_int64)((x * volume) >> 8);
44734}
44735
44736static MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume)
44737{
44738 return (ma_int64)((x * volume) >> 8);
44739}
44740
44741static MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume)
44742{
44743 return x * volume;
44744}
44745
44746
44747MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume)
44748{
44749 ma_uint64 iSample;
44750 ma_int16 volumeFixed;
44751
44752 MA_ASSERT(pDst != NULL);
44753 MA_ASSERT(pSrc != NULL);
44754
44755 volumeFixed = ma_float_to_fixed_16(volume);
44756
44757 for (iSample = 0; iSample < count; iSample += 1) {
44758 pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed));
44759 }
44760}
44761
44762MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume)
44763{
44764 ma_uint64 iSample;
44765 ma_int16 volumeFixed;
44766
44767 MA_ASSERT(pDst != NULL);
44768 MA_ASSERT(pSrc != NULL);
44769
44770 volumeFixed = ma_float_to_fixed_16(volume);
44771
44772 for (iSample = 0; iSample < count; iSample += 1) {
44773 pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed));
44774 }
44775}
44776
44777MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume)
44778{
44779 ma_uint64 iSample;
44780 ma_int16 volumeFixed;
44781
44782 MA_ASSERT(pDst != NULL);
44783 MA_ASSERT(pSrc != NULL);
44784
44785 volumeFixed = ma_float_to_fixed_16(volume);
44786
44787 for (iSample = 0; iSample < count; iSample += 1) {
44788 ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed));
44789 pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0);
44790 pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8);
44791 pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);
44792 }
44793}
44794
44795MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume)
44796{
44797 ma_uint64 iSample;
44798 ma_int16 volumeFixed;
44799
44800 MA_ASSERT(pDst != NULL);
44801 MA_ASSERT(pSrc != NULL);
44802
44803 volumeFixed = ma_float_to_fixed_16(volume);
44804
44805 for (iSample = 0; iSample < count; iSample += 1) {
44806 pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed));
44807 }
44808}
44809
44810MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume)
44811{
44812 ma_uint64 iSample;
44813
44814 MA_ASSERT(pDst != NULL);
44815 MA_ASSERT(pSrc != NULL);
44816
44817 /* For the f32 case we need to make sure this supports in-place processing where the input and output buffers are the same. */
44818
44819 for (iSample = 0; iSample < count; iSample += 1) {
44820 pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume));
44821 }
44822}
44823
44824MA_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)
44825{
44826 MA_ASSERT(pDst != NULL);
44827 MA_ASSERT(pSrc != NULL);
44828
44829 if (volume == 1) {
44830 ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels); /* Optimized case for volume = 1. */
44831 } else if (volume == 0) {
44832 ma_silence_pcm_frames(pDst, frameCount, format, channels); /* Optimized case for volume = 0. */
44833 } else {
44834 ma_uint64 sampleCount = frameCount * channels;
44835
44836 switch (format) {
44837 case ma_format_u8: ma_copy_and_apply_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break;
44838 case ma_format_s16: ma_copy_and_apply_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break;
44839 case ma_format_s24: ma_copy_and_apply_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break;
44840 case ma_format_s32: ma_copy_and_apply_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break;
44841 case ma_format_f32: ma_copy_and_apply_volume_and_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break;
44842
44843 /* 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. */
44844 case ma_format_unknown:
44845 case ma_format_count:
44846 break;
44847 }
44848 }
44849}
44850
44851
44852
44853MA_API float ma_volume_linear_to_db(float factor)
44854{
44855 return 20*ma_log10f(factor);
44856}
44857
44858MA_API float ma_volume_db_to_linear(float gain)
44859{
44860 return ma_powf(10, gain/20.0f);
44861}
44862
44863
44864MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume)
44865{
44866 ma_uint64 iSample;
44867 ma_uint64 sampleCount;
44868
44869 if (pDst == NULL || pSrc == NULL || channels == 0) {
44870 return MA_INVALID_ARGS;
44871 }
44872
44873 if (volume == 0) {
44874 return MA_SUCCESS; /* No changes if the volume is 0. */
44875 }
44876
44877 sampleCount = frameCount * channels;
44878
44879 if (volume == 1) {
44880 for (iSample = 0; iSample < sampleCount; iSample += 1) {
44881 pDst[iSample] += pSrc[iSample];
44882 }
44883 } else {
44884 for (iSample = 0; iSample < sampleCount; iSample += 1) {
44885 pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume);
44886 }
44887 }
44888
44889 return MA_SUCCESS;
44890}
44891
44892
44893
44894/**************************************************************************************************************************************************************
44895
44896Format Conversion
44897
44898**************************************************************************************************************************************************************/
44899
44900static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x)
44901{
44902 return (ma_int16)(x * 32767.0f);
44903}
44904
44905static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x)
44906{
44907 return (ma_int16)((ma_int16)x - 128);
44908}
44909
44910static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x)
44911{
44912 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. */
44913}
44914
44915static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24)
44916{
44917 s24[0] = (ma_uint8)((x & 0x000000FF) >> 0);
44918 s24[1] = (ma_uint8)((x & 0x0000FF00) >> 8);
44919 s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16);
44920}
44921
44922
44923/* u8 */
44924MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44925{
44926 (void)ditherMode;
44927 ma_copy_memory_64(dst, src, count * sizeof(ma_uint8));
44928}
44929
44930
44931static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44932{
44933 ma_int16* dst_s16 = (ma_int16*)dst;
44934 const ma_uint8* src_u8 = (const ma_uint8*)src;
44935
44936 ma_uint64 i;
44937 for (i = 0; i < count; i += 1) {
44938 ma_int16 x = src_u8[i];
44939 x = (ma_int16)(x - 128);
44940 x = (ma_int16)(x << 8);
44941 dst_s16[i] = x;
44942 }
44943
44944 (void)ditherMode;
44945}
44946
44947static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44948{
44949 ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
44950}
44951
44952#if defined(MA_SUPPORT_SSE2)
44953static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44954{
44955 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
44956}
44957#endif
44958#if defined(MA_SUPPORT_NEON)
44959static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44960{
44961 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
44962}
44963#endif
44964
44965MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44966{
44967#ifdef MA_USE_REFERENCE_CONVERSION_APIS
44968 ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
44969#else
44970 # if defined(MA_SUPPORT_SSE2)
44971 if (ma_has_sse2()) {
44972 ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode);
44973 } else
44974 #elif defined(MA_SUPPORT_NEON)
44975 if (ma_has_neon()) {
44976 ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode);
44977 } else
44978 #endif
44979 {
44980 ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
44981 }
44982#endif
44983}
44984
44985
44986static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
44987{
44988 ma_uint8* dst_s24 = (ma_uint8*)dst;
44989 const ma_uint8* src_u8 = (const ma_uint8*)src;
44990
44991 ma_uint64 i;
44992 for (i = 0; i < count; i += 1) {
44993 ma_int16 x = src_u8[i];
44994 x = (ma_int16)(x - 128);
44995
44996 dst_s24[i*3+0] = 0;
44997 dst_s24[i*3+1] = 0;
44998 dst_s24[i*3+2] = (ma_uint8)((ma_int8)x);
44999 }
45000
45001 (void)ditherMode;
45002}
45003
45004static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45005{
45006 ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
45007}
45008
45009#if defined(MA_SUPPORT_SSE2)
45010static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45011{
45012 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
45013}
45014#endif
45015#if defined(MA_SUPPORT_NEON)
45016static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45017{
45018 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
45019}
45020#endif
45021
45022MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45023{
45024#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45025 ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
45026#else
45027 # if defined(MA_SUPPORT_SSE2)
45028 if (ma_has_sse2()) {
45029 ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode);
45030 } else
45031 #elif defined(MA_SUPPORT_NEON)
45032 if (ma_has_neon()) {
45033 ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode);
45034 } else
45035 #endif
45036 {
45037 ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
45038 }
45039#endif
45040}
45041
45042
45043static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45044{
45045 ma_int32* dst_s32 = (ma_int32*)dst;
45046 const ma_uint8* src_u8 = (const ma_uint8*)src;
45047
45048 ma_uint64 i;
45049 for (i = 0; i < count; i += 1) {
45050 ma_int32 x = src_u8[i];
45051 x = x - 128;
45052 x = x << 24;
45053 dst_s32[i] = x;
45054 }
45055
45056 (void)ditherMode;
45057}
45058
45059static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45060{
45061 ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
45062}
45063
45064#if defined(MA_SUPPORT_SSE2)
45065static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45066{
45067 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
45068}
45069#endif
45070#if defined(MA_SUPPORT_NEON)
45071static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45072{
45073 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
45074}
45075#endif
45076
45077MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45078{
45079#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45080 ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
45081#else
45082 # if defined(MA_SUPPORT_SSE2)
45083 if (ma_has_sse2()) {
45084 ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode);
45085 } else
45086 #elif defined(MA_SUPPORT_NEON)
45087 if (ma_has_neon()) {
45088 ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode);
45089 } else
45090 #endif
45091 {
45092 ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
45093 }
45094#endif
45095}
45096
45097
45098static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45099{
45100 float* dst_f32 = (float*)dst;
45101 const ma_uint8* src_u8 = (const ma_uint8*)src;
45102
45103 ma_uint64 i;
45104 for (i = 0; i < count; i += 1) {
45105 float x = (float)src_u8[i];
45106 x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */
45107 x = x - 1; /* 0..2 to -1..1 */
45108
45109 dst_f32[i] = x;
45110 }
45111
45112 (void)ditherMode;
45113}
45114
45115static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45116{
45117 ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
45118}
45119
45120#if defined(MA_SUPPORT_SSE2)
45121static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45122{
45123 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
45124}
45125#endif
45126#if defined(MA_SUPPORT_NEON)
45127static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45128{
45129 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
45130}
45131#endif
45132
45133MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45134{
45135#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45136 ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
45137#else
45138 # if defined(MA_SUPPORT_SSE2)
45139 if (ma_has_sse2()) {
45140 ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode);
45141 } else
45142 #elif defined(MA_SUPPORT_NEON)
45143 if (ma_has_neon()) {
45144 ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode);
45145 } else
45146 #endif
45147 {
45148 ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
45149 }
45150#endif
45151}
45152
45153
45154#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45155static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
45156{
45157 ma_uint8* dst_u8 = (ma_uint8*)dst;
45158 const ma_uint8** src_u8 = (const ma_uint8**)src;
45159
45160 ma_uint64 iFrame;
45161 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
45162 ma_uint32 iChannel;
45163 for (iChannel = 0; iChannel < channels; iChannel += 1) {
45164 dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
45165 }
45166 }
45167}
45168#else
45169static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
45170{
45171 ma_uint8* dst_u8 = (ma_uint8*)dst;
45172 const ma_uint8** src_u8 = (const ma_uint8**)src;
45173
45174 if (channels == 1) {
45175 ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8));
45176 } else if (channels == 2) {
45177 ma_uint64 iFrame;
45178 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
45179 dst_u8[iFrame*2 + 0] = src_u8[0][iFrame];
45180 dst_u8[iFrame*2 + 1] = src_u8[1][iFrame];
45181 }
45182 } else {
45183 ma_uint64 iFrame;
45184 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
45185 ma_uint32 iChannel;
45186 for (iChannel = 0; iChannel < channels; iChannel += 1) {
45187 dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
45188 }
45189 }
45190 }
45191}
45192#endif
45193
45194MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
45195{
45196#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45197 ma_pcm_interleave_u8__reference(dst, src, frameCount, channels);
45198#else
45199 ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels);
45200#endif
45201}
45202
45203
45204static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
45205{
45206 ma_uint8** dst_u8 = (ma_uint8**)dst;
45207 const ma_uint8* src_u8 = (const ma_uint8*)src;
45208
45209 ma_uint64 iFrame;
45210 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
45211 ma_uint32 iChannel;
45212 for (iChannel = 0; iChannel < channels; iChannel += 1) {
45213 dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel];
45214 }
45215 }
45216}
45217
45218static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
45219{
45220 ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
45221}
45222
45223MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
45224{
45225#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45226 ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
45227#else
45228 ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels);
45229#endif
45230}
45231
45232
45233/* s16 */
45234static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45235{
45236 ma_uint8* dst_u8 = (ma_uint8*)dst;
45237 const ma_int16* src_s16 = (const ma_int16*)src;
45238
45239 if (ditherMode == ma_dither_mode_none) {
45240 ma_uint64 i;
45241 for (i = 0; i < count; i += 1) {
45242 ma_int16 x = src_s16[i];
45243 x = (ma_int16)(x >> 8);
45244 x = (ma_int16)(x + 128);
45245 dst_u8[i] = (ma_uint8)x;
45246 }
45247 } else {
45248 ma_uint64 i;
45249 for (i = 0; i < count; i += 1) {
45250 ma_int16 x = src_s16[i];
45251
45252 /* Dither. Don't overflow. */
45253 ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F);
45254 if ((x + dither) <= 0x7FFF) {
45255 x = (ma_int16)(x + dither);
45256 } else {
45257 x = 0x7FFF;
45258 }
45259
45260 x = (ma_int16)(x >> 8);
45261 x = (ma_int16)(x + 128);
45262 dst_u8[i] = (ma_uint8)x;
45263 }
45264 }
45265}
45266
45267static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45268{
45269 ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
45270}
45271
45272#if defined(MA_SUPPORT_SSE2)
45273static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45274{
45275 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
45276}
45277#endif
45278#if defined(MA_SUPPORT_NEON)
45279static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45280{
45281 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
45282}
45283#endif
45284
45285MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45286{
45287#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45288 ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
45289#else
45290 # if defined(MA_SUPPORT_SSE2)
45291 if (ma_has_sse2()) {
45292 ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode);
45293 } else
45294 #elif defined(MA_SUPPORT_NEON)
45295 if (ma_has_neon()) {
45296 ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode);
45297 } else
45298 #endif
45299 {
45300 ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
45301 }
45302#endif
45303}
45304
45305
45306MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45307{
45308 (void)ditherMode;
45309 ma_copy_memory_64(dst, src, count * sizeof(ma_int16));
45310}
45311
45312
45313static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45314{
45315 ma_uint8* dst_s24 = (ma_uint8*)dst;
45316 const ma_int16* src_s16 = (const ma_int16*)src;
45317
45318 ma_uint64 i;
45319 for (i = 0; i < count; i += 1) {
45320 dst_s24[i*3+0] = 0;
45321 dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF);
45322 dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8);
45323 }
45324
45325 (void)ditherMode;
45326}
45327
45328static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45329{
45330 ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
45331}
45332
45333#if defined(MA_SUPPORT_SSE2)
45334static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45335{
45336 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
45337}
45338#endif
45339#if defined(MA_SUPPORT_NEON)
45340static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45341{
45342 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
45343}
45344#endif
45345
45346MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45347{
45348#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45349 ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
45350#else
45351 # if defined(MA_SUPPORT_SSE2)
45352 if (ma_has_sse2()) {
45353 ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode);
45354 } else
45355 #elif defined(MA_SUPPORT_NEON)
45356 if (ma_has_neon()) {
45357 ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode);
45358 } else
45359 #endif
45360 {
45361 ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
45362 }
45363#endif
45364}
45365
45366
45367static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45368{
45369 ma_int32* dst_s32 = (ma_int32*)dst;
45370 const ma_int16* src_s16 = (const ma_int16*)src;
45371
45372 ma_uint64 i;
45373 for (i = 0; i < count; i += 1) {
45374 dst_s32[i] = src_s16[i] << 16;
45375 }
45376
45377 (void)ditherMode;
45378}
45379
45380static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45381{
45382 ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
45383}
45384
45385#if defined(MA_SUPPORT_SSE2)
45386static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45387{
45388 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
45389}
45390#endif
45391#if defined(MA_SUPPORT_NEON)
45392static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45393{
45394 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
45395}
45396#endif
45397
45398MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45399{
45400#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45401 ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
45402#else
45403 # if defined(MA_SUPPORT_SSE2)
45404 if (ma_has_sse2()) {
45405 ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode);
45406 } else
45407 #elif defined(MA_SUPPORT_NEON)
45408 if (ma_has_neon()) {
45409 ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode);
45410 } else
45411 #endif
45412 {
45413 ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
45414 }
45415#endif
45416}
45417
45418
45419static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45420{
45421 float* dst_f32 = (float*)dst;
45422 const ma_int16* src_s16 = (const ma_int16*)src;
45423
45424 ma_uint64 i;
45425 for (i = 0; i < count; i += 1) {
45426 float x = (float)src_s16[i];
45427
45428#if 0
45429 /* The accurate way. */
45430 x = x + 32768.0f; /* -32768..32767 to 0..65535 */
45431 x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */
45432 x = x - 1; /* 0..2 to -1..1 */
45433#else
45434 /* The fast way. */
45435 x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */
45436#endif
45437
45438 dst_f32[i] = x;
45439 }
45440
45441 (void)ditherMode;
45442}
45443
45444static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45445{
45446 ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
45447}
45448
45449#if defined(MA_SUPPORT_SSE2)
45450static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45451{
45452 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
45453}
45454#endif
45455#if defined(MA_SUPPORT_NEON)
45456static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45457{
45458 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
45459}
45460#endif
45461
45462MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45463{
45464#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45465 ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
45466#else
45467 # if defined(MA_SUPPORT_SSE2)
45468 if (ma_has_sse2()) {
45469 ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode);
45470 } else
45471 #elif defined(MA_SUPPORT_NEON)
45472 if (ma_has_neon()) {
45473 ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode);
45474 } else
45475 #endif
45476 {
45477 ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
45478 }
45479#endif
45480}
45481
45482
45483static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
45484{
45485 ma_int16* dst_s16 = (ma_int16*)dst;
45486 const ma_int16** src_s16 = (const ma_int16**)src;
45487
45488 ma_uint64 iFrame;
45489 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
45490 ma_uint32 iChannel;
45491 for (iChannel = 0; iChannel < channels; iChannel += 1) {
45492 dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame];
45493 }
45494 }
45495}
45496
45497static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
45498{
45499 ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
45500}
45501
45502MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
45503{
45504#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45505 ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
45506#else
45507 ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels);
45508#endif
45509}
45510
45511
45512static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
45513{
45514 ma_int16** dst_s16 = (ma_int16**)dst;
45515 const ma_int16* src_s16 = (const ma_int16*)src;
45516
45517 ma_uint64 iFrame;
45518 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
45519 ma_uint32 iChannel;
45520 for (iChannel = 0; iChannel < channels; iChannel += 1) {
45521 dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel];
45522 }
45523 }
45524}
45525
45526static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
45527{
45528 ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
45529}
45530
45531MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
45532{
45533#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45534 ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
45535#else
45536 ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels);
45537#endif
45538}
45539
45540
45541/* s24 */
45542static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45543{
45544 ma_uint8* dst_u8 = (ma_uint8*)dst;
45545 const ma_uint8* src_s24 = (const ma_uint8*)src;
45546
45547 if (ditherMode == ma_dither_mode_none) {
45548 ma_uint64 i;
45549 for (i = 0; i < count; i += 1) {
45550 dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128);
45551 }
45552 } else {
45553 ma_uint64 i;
45554 for (i = 0; i < count; i += 1) {
45555 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);
45556
45557 /* Dither. Don't overflow. */
45558 ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
45559 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
45560 x = x + dither;
45561 } else {
45562 x = 0x7FFFFFFF;
45563 }
45564
45565 x = x >> 24;
45566 x = x + 128;
45567 dst_u8[i] = (ma_uint8)x;
45568 }
45569 }
45570}
45571
45572static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45573{
45574 ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
45575}
45576
45577#if defined(MA_SUPPORT_SSE2)
45578static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45579{
45580 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
45581}
45582#endif
45583#if defined(MA_SUPPORT_NEON)
45584static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45585{
45586 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
45587}
45588#endif
45589
45590MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45591{
45592#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45593 ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
45594#else
45595 # if defined(MA_SUPPORT_SSE2)
45596 if (ma_has_sse2()) {
45597 ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode);
45598 } else
45599 #elif defined(MA_SUPPORT_NEON)
45600 if (ma_has_neon()) {
45601 ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode);
45602 } else
45603 #endif
45604 {
45605 ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
45606 }
45607#endif
45608}
45609
45610
45611static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45612{
45613 ma_int16* dst_s16 = (ma_int16*)dst;
45614 const ma_uint8* src_s24 = (const ma_uint8*)src;
45615
45616 if (ditherMode == ma_dither_mode_none) {
45617 ma_uint64 i;
45618 for (i = 0; i < count; i += 1) {
45619 ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]);
45620 ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8);
45621 dst_s16[i] = (ma_int16)(dst_lo | dst_hi);
45622 }
45623 } else {
45624 ma_uint64 i;
45625 for (i = 0; i < count; i += 1) {
45626 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);
45627
45628 /* Dither. Don't overflow. */
45629 ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
45630 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
45631 x = x + dither;
45632 } else {
45633 x = 0x7FFFFFFF;
45634 }
45635
45636 x = x >> 16;
45637 dst_s16[i] = (ma_int16)x;
45638 }
45639 }
45640}
45641
45642static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45643{
45644 ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
45645}
45646
45647#if defined(MA_SUPPORT_SSE2)
45648static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45649{
45650 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
45651}
45652#endif
45653#if defined(MA_SUPPORT_NEON)
45654static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45655{
45656 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
45657}
45658#endif
45659
45660MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45661{
45662#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45663 ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
45664#else
45665 # if defined(MA_SUPPORT_SSE2)
45666 if (ma_has_sse2()) {
45667 ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode);
45668 } else
45669 #elif defined(MA_SUPPORT_NEON)
45670 if (ma_has_neon()) {
45671 ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode);
45672 } else
45673 #endif
45674 {
45675 ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
45676 }
45677#endif
45678}
45679
45680
45681MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45682{
45683 (void)ditherMode;
45684
45685 ma_copy_memory_64(dst, src, count * 3);
45686}
45687
45688
45689static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45690{
45691 ma_int32* dst_s32 = (ma_int32*)dst;
45692 const ma_uint8* src_s24 = (const ma_uint8*)src;
45693
45694 ma_uint64 i;
45695 for (i = 0; i < count; i += 1) {
45696 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);
45697 }
45698
45699 (void)ditherMode;
45700}
45701
45702static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45703{
45704 ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
45705}
45706
45707#if defined(MA_SUPPORT_SSE2)
45708static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45709{
45710 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
45711}
45712#endif
45713#if defined(MA_SUPPORT_NEON)
45714static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45715{
45716 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
45717}
45718#endif
45719
45720MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45721{
45722#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45723 ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
45724#else
45725 # if defined(MA_SUPPORT_SSE2)
45726 if (ma_has_sse2()) {
45727 ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode);
45728 } else
45729 #elif defined(MA_SUPPORT_NEON)
45730 if (ma_has_neon()) {
45731 ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode);
45732 } else
45733 #endif
45734 {
45735 ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
45736 }
45737#endif
45738}
45739
45740
45741static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45742{
45743 float* dst_f32 = (float*)dst;
45744 const ma_uint8* src_s24 = (const ma_uint8*)src;
45745
45746 ma_uint64 i;
45747 for (i = 0; i < count; i += 1) {
45748 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);
45749
45750#if 0
45751 /* The accurate way. */
45752 x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */
45753 x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */
45754 x = x - 1; /* 0..2 to -1..1 */
45755#else
45756 /* The fast way. */
45757 x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */
45758#endif
45759
45760 dst_f32[i] = x;
45761 }
45762
45763 (void)ditherMode;
45764}
45765
45766static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45767{
45768 ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
45769}
45770
45771#if defined(MA_SUPPORT_SSE2)
45772static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45773{
45774 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
45775}
45776#endif
45777#if defined(MA_SUPPORT_NEON)
45778static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45779{
45780 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
45781}
45782#endif
45783
45784MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45785{
45786#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45787 ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
45788#else
45789 # if defined(MA_SUPPORT_SSE2)
45790 if (ma_has_sse2()) {
45791 ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode);
45792 } else
45793 #elif defined(MA_SUPPORT_NEON)
45794 if (ma_has_neon()) {
45795 ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode);
45796 } else
45797 #endif
45798 {
45799 ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
45800 }
45801#endif
45802}
45803
45804
45805static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
45806{
45807 ma_uint8* dst8 = (ma_uint8*)dst;
45808 const ma_uint8** src8 = (const ma_uint8**)src;
45809
45810 ma_uint64 iFrame;
45811 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
45812 ma_uint32 iChannel;
45813 for (iChannel = 0; iChannel < channels; iChannel += 1) {
45814 dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0];
45815 dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1];
45816 dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2];
45817 }
45818 }
45819}
45820
45821static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
45822{
45823 ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
45824}
45825
45826MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
45827{
45828#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45829 ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
45830#else
45831 ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels);
45832#endif
45833}
45834
45835
45836static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
45837{
45838 ma_uint8** dst8 = (ma_uint8**)dst;
45839 const ma_uint8* src8 = (const ma_uint8*)src;
45840
45841 ma_uint32 iFrame;
45842 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
45843 ma_uint32 iChannel;
45844 for (iChannel = 0; iChannel < channels; iChannel += 1) {
45845 dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0];
45846 dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1];
45847 dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2];
45848 }
45849 }
45850}
45851
45852static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
45853{
45854 ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
45855}
45856
45857MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
45858{
45859#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45860 ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
45861#else
45862 ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels);
45863#endif
45864}
45865
45866
45867
45868/* s32 */
45869static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45870{
45871 ma_uint8* dst_u8 = (ma_uint8*)dst;
45872 const ma_int32* src_s32 = (const ma_int32*)src;
45873
45874 if (ditherMode == ma_dither_mode_none) {
45875 ma_uint64 i;
45876 for (i = 0; i < count; i += 1) {
45877 ma_int32 x = src_s32[i];
45878 x = x >> 24;
45879 x = x + 128;
45880 dst_u8[i] = (ma_uint8)x;
45881 }
45882 } else {
45883 ma_uint64 i;
45884 for (i = 0; i < count; i += 1) {
45885 ma_int32 x = src_s32[i];
45886
45887 /* Dither. Don't overflow. */
45888 ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
45889 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
45890 x = x + dither;
45891 } else {
45892 x = 0x7FFFFFFF;
45893 }
45894
45895 x = x >> 24;
45896 x = x + 128;
45897 dst_u8[i] = (ma_uint8)x;
45898 }
45899 }
45900}
45901
45902static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45903{
45904 ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
45905}
45906
45907#if defined(MA_SUPPORT_SSE2)
45908static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45909{
45910 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
45911}
45912#endif
45913#if defined(MA_SUPPORT_NEON)
45914static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45915{
45916 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
45917}
45918#endif
45919
45920MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45921{
45922#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45923 ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
45924#else
45925 # if defined(MA_SUPPORT_SSE2)
45926 if (ma_has_sse2()) {
45927 ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode);
45928 } else
45929 #elif defined(MA_SUPPORT_NEON)
45930 if (ma_has_neon()) {
45931 ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode);
45932 } else
45933 #endif
45934 {
45935 ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
45936 }
45937#endif
45938}
45939
45940
45941static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45942{
45943 ma_int16* dst_s16 = (ma_int16*)dst;
45944 const ma_int32* src_s32 = (const ma_int32*)src;
45945
45946 if (ditherMode == ma_dither_mode_none) {
45947 ma_uint64 i;
45948 for (i = 0; i < count; i += 1) {
45949 ma_int32 x = src_s32[i];
45950 x = x >> 16;
45951 dst_s16[i] = (ma_int16)x;
45952 }
45953 } else {
45954 ma_uint64 i;
45955 for (i = 0; i < count; i += 1) {
45956 ma_int32 x = src_s32[i];
45957
45958 /* Dither. Don't overflow. */
45959 ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
45960 if ((ma_int64)x + dither <= 0x7FFFFFFF) {
45961 x = x + dither;
45962 } else {
45963 x = 0x7FFFFFFF;
45964 }
45965
45966 x = x >> 16;
45967 dst_s16[i] = (ma_int16)x;
45968 }
45969 }
45970}
45971
45972static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45973{
45974 ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
45975}
45976
45977#if defined(MA_SUPPORT_SSE2)
45978static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45979{
45980 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
45981}
45982#endif
45983#if defined(MA_SUPPORT_NEON)
45984static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45985{
45986 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
45987}
45988#endif
45989
45990MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
45991{
45992#ifdef MA_USE_REFERENCE_CONVERSION_APIS
45993 ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
45994#else
45995 # if defined(MA_SUPPORT_SSE2)
45996 if (ma_has_sse2()) {
45997 ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode);
45998 } else
45999 #elif defined(MA_SUPPORT_NEON)
46000 if (ma_has_neon()) {
46001 ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode);
46002 } else
46003 #endif
46004 {
46005 ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
46006 }
46007#endif
46008}
46009
46010
46011static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46012{
46013 ma_uint8* dst_s24 = (ma_uint8*)dst;
46014 const ma_int32* src_s32 = (const ma_int32*)src;
46015
46016 ma_uint64 i;
46017 for (i = 0; i < count; i += 1) {
46018 ma_uint32 x = (ma_uint32)src_s32[i];
46019 dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8);
46020 dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16);
46021 dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24);
46022 }
46023
46024 (void)ditherMode; /* No dithering for s32 -> s24. */
46025}
46026
46027static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46028{
46029 ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
46030}
46031
46032#if defined(MA_SUPPORT_SSE2)
46033static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46034{
46035 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
46036}
46037#endif
46038#if defined(MA_SUPPORT_NEON)
46039static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46040{
46041 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
46042}
46043#endif
46044
46045MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46046{
46047#ifdef MA_USE_REFERENCE_CONVERSION_APIS
46048 ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
46049#else
46050 # if defined(MA_SUPPORT_SSE2)
46051 if (ma_has_sse2()) {
46052 ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode);
46053 } else
46054 #elif defined(MA_SUPPORT_NEON)
46055 if (ma_has_neon()) {
46056 ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode);
46057 } else
46058 #endif
46059 {
46060 ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
46061 }
46062#endif
46063}
46064
46065
46066MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46067{
46068 (void)ditherMode;
46069
46070 ma_copy_memory_64(dst, src, count * sizeof(ma_int32));
46071}
46072
46073
46074static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46075{
46076 float* dst_f32 = (float*)dst;
46077 const ma_int32* src_s32 = (const ma_int32*)src;
46078
46079 ma_uint64 i;
46080 for (i = 0; i < count; i += 1) {
46081 double x = src_s32[i];
46082
46083#if 0
46084 x = x + 2147483648.0;
46085 x = x * 0.0000000004656612873077392578125;
46086 x = x - 1;
46087#else
46088 x = x / 2147483648.0;
46089#endif
46090
46091 dst_f32[i] = (float)x;
46092 }
46093
46094 (void)ditherMode; /* No dithering for s32 -> f32. */
46095}
46096
46097static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46098{
46099 ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
46100}
46101
46102#if defined(MA_SUPPORT_SSE2)
46103static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46104{
46105 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
46106}
46107#endif
46108#if defined(MA_SUPPORT_NEON)
46109static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46110{
46111 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
46112}
46113#endif
46114
46115MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46116{
46117#ifdef MA_USE_REFERENCE_CONVERSION_APIS
46118 ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
46119#else
46120 # if defined(MA_SUPPORT_SSE2)
46121 if (ma_has_sse2()) {
46122 ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode);
46123 } else
46124 #elif defined(MA_SUPPORT_NEON)
46125 if (ma_has_neon()) {
46126 ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode);
46127 } else
46128 #endif
46129 {
46130 ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
46131 }
46132#endif
46133}
46134
46135
46136static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
46137{
46138 ma_int32* dst_s32 = (ma_int32*)dst;
46139 const ma_int32** src_s32 = (const ma_int32**)src;
46140
46141 ma_uint64 iFrame;
46142 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
46143 ma_uint32 iChannel;
46144 for (iChannel = 0; iChannel < channels; iChannel += 1) {
46145 dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame];
46146 }
46147 }
46148}
46149
46150static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
46151{
46152 ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
46153}
46154
46155MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
46156{
46157#ifdef MA_USE_REFERENCE_CONVERSION_APIS
46158 ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
46159#else
46160 ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels);
46161#endif
46162}
46163
46164
46165static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
46166{
46167 ma_int32** dst_s32 = (ma_int32**)dst;
46168 const ma_int32* src_s32 = (const ma_int32*)src;
46169
46170 ma_uint64 iFrame;
46171 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
46172 ma_uint32 iChannel;
46173 for (iChannel = 0; iChannel < channels; iChannel += 1) {
46174 dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel];
46175 }
46176 }
46177}
46178
46179static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
46180{
46181 ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
46182}
46183
46184MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
46185{
46186#ifdef MA_USE_REFERENCE_CONVERSION_APIS
46187 ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
46188#else
46189 ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels);
46190#endif
46191}
46192
46193
46194/* f32 */
46195static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46196{
46197 ma_uint64 i;
46198
46199 ma_uint8* dst_u8 = (ma_uint8*)dst;
46200 const float* src_f32 = (const float*)src;
46201
46202 float ditherMin = 0;
46203 float ditherMax = 0;
46204 if (ditherMode != ma_dither_mode_none) {
46205 ditherMin = 1.0f / -128;
46206 ditherMax = 1.0f / 127;
46207 }
46208
46209 for (i = 0; i < count; i += 1) {
46210 float x = src_f32[i];
46211 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
46212 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
46213 x = x + 1; /* -1..1 to 0..2 */
46214 x = x * 127.5f; /* 0..2 to 0..255 */
46215
46216 dst_u8[i] = (ma_uint8)x;
46217 }
46218}
46219
46220static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46221{
46222 ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
46223}
46224
46225#if defined(MA_SUPPORT_SSE2)
46226static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46227{
46228 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
46229}
46230#endif
46231#if defined(MA_SUPPORT_NEON)
46232static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46233{
46234 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
46235}
46236#endif
46237
46238MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46239{
46240#ifdef MA_USE_REFERENCE_CONVERSION_APIS
46241 ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
46242#else
46243 # if defined(MA_SUPPORT_SSE2)
46244 if (ma_has_sse2()) {
46245 ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode);
46246 } else
46247 #elif defined(MA_SUPPORT_NEON)
46248 if (ma_has_neon()) {
46249 ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode);
46250 } else
46251 #endif
46252 {
46253 ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
46254 }
46255#endif
46256}
46257
46258#ifdef MA_USE_REFERENCE_CONVERSION_APIS
46259static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46260{
46261 ma_uint64 i;
46262
46263 ma_int16* dst_s16 = (ma_int16*)dst;
46264 const float* src_f32 = (const float*)src;
46265
46266 float ditherMin = 0;
46267 float ditherMax = 0;
46268 if (ditherMode != ma_dither_mode_none) {
46269 ditherMin = 1.0f / -32768;
46270 ditherMax = 1.0f / 32767;
46271 }
46272
46273 for (i = 0; i < count; i += 1) {
46274 float x = src_f32[i];
46275 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
46276 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
46277
46278#if 0
46279 /* The accurate way. */
46280 x = x + 1; /* -1..1 to 0..2 */
46281 x = x * 32767.5f; /* 0..2 to 0..65535 */
46282 x = x - 32768.0f; /* 0...65535 to -32768..32767 */
46283#else
46284 /* The fast way. */
46285 x = x * 32767.0f; /* -1..1 to -32767..32767 */
46286#endif
46287
46288 dst_s16[i] = (ma_int16)x;
46289 }
46290}
46291#else
46292static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46293{
46294 ma_uint64 i;
46295 ma_uint64 i4;
46296 ma_uint64 count4;
46297
46298 ma_int16* dst_s16 = (ma_int16*)dst;
46299 const float* src_f32 = (const float*)src;
46300
46301 float ditherMin = 0;
46302 float ditherMax = 0;
46303 if (ditherMode != ma_dither_mode_none) {
46304 ditherMin = 1.0f / -32768;
46305 ditherMax = 1.0f / 32767;
46306 }
46307
46308 /* Unrolled. */
46309 i = 0;
46310 count4 = count >> 2;
46311 for (i4 = 0; i4 < count4; i4 += 1) {
46312 float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
46313 float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
46314 float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
46315 float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
46316
46317 float x0 = src_f32[i+0];
46318 float x1 = src_f32[i+1];
46319 float x2 = src_f32[i+2];
46320 float x3 = src_f32[i+3];
46321
46322 x0 = x0 + d0;
46323 x1 = x1 + d1;
46324 x2 = x2 + d2;
46325 x3 = x3 + d3;
46326
46327 x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
46328 x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
46329 x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
46330 x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
46331
46332 x0 = x0 * 32767.0f;
46333 x1 = x1 * 32767.0f;
46334 x2 = x2 * 32767.0f;
46335 x3 = x3 * 32767.0f;
46336
46337 dst_s16[i+0] = (ma_int16)x0;
46338 dst_s16[i+1] = (ma_int16)x1;
46339 dst_s16[i+2] = (ma_int16)x2;
46340 dst_s16[i+3] = (ma_int16)x3;
46341
46342 i += 4;
46343 }
46344
46345 /* Leftover. */
46346 for (; i < count; i += 1) {
46347 float x = src_f32[i];
46348 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
46349 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
46350 x = x * 32767.0f; /* -1..1 to -32767..32767 */
46351
46352 dst_s16[i] = (ma_int16)x;
46353 }
46354}
46355
46356#if defined(MA_SUPPORT_SSE2)
46357static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46358{
46359 ma_uint64 i;
46360 ma_uint64 i8;
46361 ma_uint64 count8;
46362 ma_int16* dst_s16;
46363 const float* src_f32;
46364 float ditherMin;
46365 float ditherMax;
46366
46367 /* Both the input and output buffers need to be aligned to 16 bytes. */
46368 if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
46369 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
46370 return;
46371 }
46372
46373 dst_s16 = (ma_int16*)dst;
46374 src_f32 = (const float*)src;
46375
46376 ditherMin = 0;
46377 ditherMax = 0;
46378 if (ditherMode != ma_dither_mode_none) {
46379 ditherMin = 1.0f / -32768;
46380 ditherMax = 1.0f / 32767;
46381 }
46382
46383 i = 0;
46384
46385 /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
46386 count8 = count >> 3;
46387 for (i8 = 0; i8 < count8; i8 += 1) {
46388 __m128 d0;
46389 __m128 d1;
46390 __m128 x0;
46391 __m128 x1;
46392
46393 if (ditherMode == ma_dither_mode_none) {
46394 d0 = _mm_set1_ps(0);
46395 d1 = _mm_set1_ps(0);
46396 } else if (ditherMode == ma_dither_mode_rectangle) {
46397 d0 = _mm_set_ps(
46398 ma_dither_f32_rectangle(ditherMin, ditherMax),
46399 ma_dither_f32_rectangle(ditherMin, ditherMax),
46400 ma_dither_f32_rectangle(ditherMin, ditherMax),
46401 ma_dither_f32_rectangle(ditherMin, ditherMax)
46402 );
46403 d1 = _mm_set_ps(
46404 ma_dither_f32_rectangle(ditherMin, ditherMax),
46405 ma_dither_f32_rectangle(ditherMin, ditherMax),
46406 ma_dither_f32_rectangle(ditherMin, ditherMax),
46407 ma_dither_f32_rectangle(ditherMin, ditherMax)
46408 );
46409 } else {
46410 d0 = _mm_set_ps(
46411 ma_dither_f32_triangle(ditherMin, ditherMax),
46412 ma_dither_f32_triangle(ditherMin, ditherMax),
46413 ma_dither_f32_triangle(ditherMin, ditherMax),
46414 ma_dither_f32_triangle(ditherMin, ditherMax)
46415 );
46416 d1 = _mm_set_ps(
46417 ma_dither_f32_triangle(ditherMin, ditherMax),
46418 ma_dither_f32_triangle(ditherMin, ditherMax),
46419 ma_dither_f32_triangle(ditherMin, ditherMax),
46420 ma_dither_f32_triangle(ditherMin, ditherMax)
46421 );
46422 }
46423
46424 x0 = *((__m128*)(src_f32 + i) + 0);
46425 x1 = *((__m128*)(src_f32 + i) + 1);
46426
46427 x0 = _mm_add_ps(x0, d0);
46428 x1 = _mm_add_ps(x1, d1);
46429
46430 x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f));
46431 x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f));
46432
46433 _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1)));
46434
46435 i += 8;
46436 }
46437
46438
46439 /* Leftover. */
46440 for (; i < count; i += 1) {
46441 float x = src_f32[i];
46442 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
46443 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
46444 x = x * 32767.0f; /* -1..1 to -32767..32767 */
46445
46446 dst_s16[i] = (ma_int16)x;
46447 }
46448}
46449#endif /* SSE2 */
46450
46451#if defined(MA_SUPPORT_NEON)
46452static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46453{
46454 ma_uint64 i;
46455 ma_uint64 i8;
46456 ma_uint64 count8;
46457 ma_int16* dst_s16;
46458 const float* src_f32;
46459 float ditherMin;
46460 float ditherMax;
46461
46462 if (!ma_has_neon()) {
46463 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
46464 return;
46465 }
46466
46467 /* Both the input and output buffers need to be aligned to 16 bytes. */
46468 if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
46469 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
46470 return;
46471 }
46472
46473 dst_s16 = (ma_int16*)dst;
46474 src_f32 = (const float*)src;
46475
46476 ditherMin = 0;
46477 ditherMax = 0;
46478 if (ditherMode != ma_dither_mode_none) {
46479 ditherMin = 1.0f / -32768;
46480 ditherMax = 1.0f / 32767;
46481 }
46482
46483 i = 0;
46484
46485 /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
46486 count8 = count >> 3;
46487 for (i8 = 0; i8 < count8; i8 += 1) {
46488 float32x4_t d0;
46489 float32x4_t d1;
46490 float32x4_t x0;
46491 float32x4_t x1;
46492 int32x4_t i0;
46493 int32x4_t i1;
46494
46495 if (ditherMode == ma_dither_mode_none) {
46496 d0 = vmovq_n_f32(0);
46497 d1 = vmovq_n_f32(0);
46498 } else if (ditherMode == ma_dither_mode_rectangle) {
46499 float d0v[4];
46500 float d1v[4];
46501
46502 d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
46503 d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
46504 d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
46505 d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
46506 d0 = vld1q_f32(d0v);
46507
46508 d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
46509 d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
46510 d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
46511 d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
46512 d1 = vld1q_f32(d1v);
46513 } else {
46514 float d0v[4];
46515 float d1v[4];
46516
46517 d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
46518 d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
46519 d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
46520 d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
46521 d0 = vld1q_f32(d0v);
46522
46523 d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
46524 d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
46525 d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
46526 d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
46527 d1 = vld1q_f32(d1v);
46528 }
46529
46530 x0 = *((float32x4_t*)(src_f32 + i) + 0);
46531 x1 = *((float32x4_t*)(src_f32 + i) + 1);
46532
46533 x0 = vaddq_f32(x0, d0);
46534 x1 = vaddq_f32(x1, d1);
46535
46536 x0 = vmulq_n_f32(x0, 32767.0f);
46537 x1 = vmulq_n_f32(x1, 32767.0f);
46538
46539 i0 = vcvtq_s32_f32(x0);
46540 i1 = vcvtq_s32_f32(x1);
46541 *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1));
46542
46543 i += 8;
46544 }
46545
46546
46547 /* Leftover. */
46548 for (; i < count; i += 1) {
46549 float x = src_f32[i];
46550 x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
46551 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
46552 x = x * 32767.0f; /* -1..1 to -32767..32767 */
46553
46554 dst_s16[i] = (ma_int16)x;
46555 }
46556}
46557#endif /* Neon */
46558#endif /* MA_USE_REFERENCE_CONVERSION_APIS */
46559
46560MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46561{
46562#ifdef MA_USE_REFERENCE_CONVERSION_APIS
46563 ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode);
46564#else
46565 # if defined(MA_SUPPORT_SSE2)
46566 if (ma_has_sse2()) {
46567 ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode);
46568 } else
46569 #elif defined(MA_SUPPORT_NEON)
46570 if (ma_has_neon()) {
46571 ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode);
46572 } else
46573 #endif
46574 {
46575 ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
46576 }
46577#endif
46578}
46579
46580
46581static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46582{
46583 ma_uint8* dst_s24 = (ma_uint8*)dst;
46584 const float* src_f32 = (const float*)src;
46585
46586 ma_uint64 i;
46587 for (i = 0; i < count; i += 1) {
46588 ma_int32 r;
46589 float x = src_f32[i];
46590 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
46591
46592#if 0
46593 /* The accurate way. */
46594 x = x + 1; /* -1..1 to 0..2 */
46595 x = x * 8388607.5f; /* 0..2 to 0..16777215 */
46596 x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */
46597#else
46598 /* The fast way. */
46599 x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */
46600#endif
46601
46602 r = (ma_int32)x;
46603 dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0);
46604 dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8);
46605 dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16);
46606 }
46607
46608 (void)ditherMode; /* No dithering for f32 -> s24. */
46609}
46610
46611static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46612{
46613 ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
46614}
46615
46616#if defined(MA_SUPPORT_SSE2)
46617static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46618{
46619 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
46620}
46621#endif
46622#if defined(MA_SUPPORT_NEON)
46623static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46624{
46625 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
46626}
46627#endif
46628
46629MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46630{
46631#ifdef MA_USE_REFERENCE_CONVERSION_APIS
46632 ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
46633#else
46634 # if defined(MA_SUPPORT_SSE2)
46635 if (ma_has_sse2()) {
46636 ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode);
46637 } else
46638 #elif defined(MA_SUPPORT_NEON)
46639 if (ma_has_neon()) {
46640 ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode);
46641 } else
46642 #endif
46643 {
46644 ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
46645 }
46646#endif
46647}
46648
46649
46650static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46651{
46652 ma_int32* dst_s32 = (ma_int32*)dst;
46653 const float* src_f32 = (const float*)src;
46654
46655 ma_uint32 i;
46656 for (i = 0; i < count; i += 1) {
46657 double x = src_f32[i];
46658 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
46659
46660#if 0
46661 /* The accurate way. */
46662 x = x + 1; /* -1..1 to 0..2 */
46663 x = x * 2147483647.5; /* 0..2 to 0..4294967295 */
46664 x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */
46665#else
46666 /* The fast way. */
46667 x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */
46668#endif
46669
46670 dst_s32[i] = (ma_int32)x;
46671 }
46672
46673 (void)ditherMode; /* No dithering for f32 -> s32. */
46674}
46675
46676static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46677{
46678 ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
46679}
46680
46681#if defined(MA_SUPPORT_SSE2)
46682static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46683{
46684 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
46685}
46686#endif
46687#if defined(MA_SUPPORT_NEON)
46688static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46689{
46690 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
46691}
46692#endif
46693
46694MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46695{
46696#ifdef MA_USE_REFERENCE_CONVERSION_APIS
46697 ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
46698#else
46699 # if defined(MA_SUPPORT_SSE2)
46700 if (ma_has_sse2()) {
46701 ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode);
46702 } else
46703 #elif defined(MA_SUPPORT_NEON)
46704 if (ma_has_neon()) {
46705 ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode);
46706 } else
46707 #endif
46708 {
46709 ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
46710 }
46711#endif
46712}
46713
46714
46715MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
46716{
46717 (void)ditherMode;
46718
46719 ma_copy_memory_64(dst, src, count * sizeof(float));
46720}
46721
46722
46723static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
46724{
46725 float* dst_f32 = (float*)dst;
46726 const float** src_f32 = (const float**)src;
46727
46728 ma_uint64 iFrame;
46729 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
46730 ma_uint32 iChannel;
46731 for (iChannel = 0; iChannel < channels; iChannel += 1) {
46732 dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame];
46733 }
46734 }
46735}
46736
46737static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
46738{
46739 ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
46740}
46741
46742MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
46743{
46744#ifdef MA_USE_REFERENCE_CONVERSION_APIS
46745 ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
46746#else
46747 ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels);
46748#endif
46749}
46750
46751
46752static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
46753{
46754 float** dst_f32 = (float**)dst;
46755 const float* src_f32 = (const float*)src;
46756
46757 ma_uint64 iFrame;
46758 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
46759 ma_uint32 iChannel;
46760 for (iChannel = 0; iChannel < channels; iChannel += 1) {
46761 dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel];
46762 }
46763 }
46764}
46765
46766static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
46767{
46768 ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
46769}
46770
46771MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
46772{
46773#ifdef MA_USE_REFERENCE_CONVERSION_APIS
46774 ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
46775#else
46776 ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels);
46777#endif
46778}
46779
46780
46781MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
46782{
46783 if (formatOut == formatIn) {
46784 ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut));
46785 return;
46786 }
46787
46788 switch (formatIn)
46789 {
46790 case ma_format_u8:
46791 {
46792 switch (formatOut)
46793 {
46794 case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return;
46795 case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return;
46796 case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return;
46797 case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return;
46798 default: break;
46799 }
46800 } break;
46801
46802 case ma_format_s16:
46803 {
46804 switch (formatOut)
46805 {
46806 case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return;
46807 case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return;
46808 case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return;
46809 case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return;
46810 default: break;
46811 }
46812 } break;
46813
46814 case ma_format_s24:
46815 {
46816 switch (formatOut)
46817 {
46818 case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return;
46819 case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return;
46820 case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return;
46821 case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return;
46822 default: break;
46823 }
46824 } break;
46825
46826 case ma_format_s32:
46827 {
46828 switch (formatOut)
46829 {
46830 case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
46831 case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
46832 case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
46833 case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return;
46834 default: break;
46835 }
46836 } break;
46837
46838 case ma_format_f32:
46839 {
46840 switch (formatOut)
46841 {
46842 case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
46843 case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
46844 case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
46845 case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return;
46846 default: break;
46847 }
46848 } break;
46849
46850 default: break;
46851 }
46852}
46853
46854MA_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)
46855{
46856 ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode);
46857}
46858
46859MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
46860{
46861 if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {
46862 return; /* Invalid args. */
46863 }
46864
46865 /* For efficiency we do this per format. */
46866 switch (format) {
46867 case ma_format_s16:
46868 {
46869 const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames;
46870 ma_uint64 iPCMFrame;
46871 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
46872 ma_uint32 iChannel;
46873 for (iChannel = 0; iChannel < channels; ++iChannel) {
46874 ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel];
46875 pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];
46876 }
46877 }
46878 } break;
46879
46880 case ma_format_f32:
46881 {
46882 const float* pSrcF32 = (const float*)pInterleavedPCMFrames;
46883 ma_uint64 iPCMFrame;
46884 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
46885 ma_uint32 iChannel;
46886 for (iChannel = 0; iChannel < channels; ++iChannel) {
46887 float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];
46888 pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];
46889 }
46890 }
46891 } break;
46892
46893 default:
46894 {
46895 ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
46896 ma_uint64 iPCMFrame;
46897 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
46898 ma_uint32 iChannel;
46899 for (iChannel = 0; iChannel < channels; ++iChannel) {
46900 void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
46901 const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
46902 memcpy(pDst, pSrc, sampleSizeInBytes);
46903 }
46904 }
46905 } break;
46906 }
46907}
46908
46909MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
46910{
46911 switch (format)
46912 {
46913 case ma_format_s16:
46914 {
46915 ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames;
46916 ma_uint64 iPCMFrame;
46917 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
46918 ma_uint32 iChannel;
46919 for (iChannel = 0; iChannel < channels; ++iChannel) {
46920 const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel];
46921 pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];
46922 }
46923 }
46924 } break;
46925
46926 case ma_format_f32:
46927 {
46928 float* pDstF32 = (float*)pInterleavedPCMFrames;
46929 ma_uint64 iPCMFrame;
46930 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
46931 ma_uint32 iChannel;
46932 for (iChannel = 0; iChannel < channels; ++iChannel) {
46933 const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];
46934 pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];
46935 }
46936 }
46937 } break;
46938
46939 default:
46940 {
46941 ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
46942 ma_uint64 iPCMFrame;
46943 for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
46944 ma_uint32 iChannel;
46945 for (iChannel = 0; iChannel < channels; ++iChannel) {
46946 void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
46947 const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
46948 memcpy(pDst, pSrc, sampleSizeInBytes);
46949 }
46950 }
46951 } break;
46952 }
46953}
46954
46955
46956/**************************************************************************************************************************************************************
46957
46958Biquad Filter
46959
46960**************************************************************************************************************************************************************/
46961#ifndef MA_BIQUAD_FIXED_POINT_SHIFT
46962#define MA_BIQUAD_FIXED_POINT_SHIFT 14
46963#endif
46964
46965static ma_int32 ma_biquad_float_to_fp(double x)
46966{
46967 return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT));
46968}
46969
46970MA_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)
46971{
46972 ma_biquad_config config;
46973
46974 MA_ZERO_OBJECT(&config);
46975 config.format = format;
46976 config.channels = channels;
46977 config.b0 = b0;
46978 config.b1 = b1;
46979 config.b2 = b2;
46980 config.a0 = a0;
46981 config.a1 = a1;
46982 config.a2 = a2;
46983
46984 return config;
46985}
46986
46987
46988typedef struct
46989{
46990 size_t sizeInBytes;
46991 size_t r1Offset;
46992 size_t r2Offset;
46993} ma_biquad_heap_layout;
46994
46995static ma_result ma_biquad_get_heap_layout(const ma_biquad_config* pConfig, ma_biquad_heap_layout* pHeapLayout)
46996{
46997 MA_ASSERT(pHeapLayout != NULL);
46998
46999 MA_ZERO_OBJECT(pHeapLayout);
47000
47001 if (pConfig == NULL) {
47002 return MA_INVALID_ARGS;
47003 }
47004
47005 if (pConfig->channels == 0) {
47006 return MA_INVALID_ARGS;
47007 }
47008
47009 pHeapLayout->sizeInBytes = 0;
47010
47011 /* R0 */
47012 pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;
47013 pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
47014
47015 /* R1 */
47016 pHeapLayout->r2Offset = pHeapLayout->sizeInBytes;
47017 pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
47018
47019 /* Make sure allocation size is aligned. */
47020 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
47021
47022 return MA_SUCCESS;
47023}
47024
47025MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes)
47026{
47027 ma_result result;
47028 ma_biquad_heap_layout heapLayout;
47029
47030 if (pHeapSizeInBytes == NULL) {
47031 return MA_INVALID_ARGS;
47032 }
47033
47034 *pHeapSizeInBytes = 0;
47035
47036 result = ma_biquad_get_heap_layout(pConfig, &heapLayout);
47037 if (result != MA_SUCCESS) {
47038 return result;
47039 }
47040
47041 *pHeapSizeInBytes = heapLayout.sizeInBytes;
47042
47043 return MA_SUCCESS;
47044}
47045
47047{
47048 ma_result result;
47049 ma_biquad_heap_layout heapLayout;
47050
47051 if (pBQ == NULL) {
47052 return MA_INVALID_ARGS;
47053 }
47054
47055 MA_ZERO_OBJECT(pBQ);
47056
47057 result = ma_biquad_get_heap_layout(pConfig, &heapLayout);
47058 if (result != MA_SUCCESS) {
47059 return result;
47060 }
47061
47062 pBQ->_pHeap = pHeap;
47063 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
47064
47065 pBQ->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);
47066 pBQ->pR2 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r2Offset);
47067
47068 return ma_biquad_reinit(pConfig, pBQ);
47069}
47070
47071MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ)
47072{
47073 ma_result result;
47074 size_t heapSizeInBytes;
47075 void* pHeap;
47076
47077 result = ma_biquad_get_heap_size(pConfig, &heapSizeInBytes);
47078 if (result != MA_SUCCESS) {
47079 return result;
47080 }
47081
47082 if (heapSizeInBytes > 0) {
47083 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
47084 if (pHeap == NULL) {
47085 return MA_OUT_OF_MEMORY;
47086 }
47087 } else {
47088 pHeap = NULL;
47089 }
47090
47091 result = ma_biquad_init_preallocated(pConfig, pHeap, pBQ);
47092 if (result != MA_SUCCESS) {
47093 ma_free(pHeap, pAllocationCallbacks);
47094 return result;
47095 }
47096
47097 pBQ->_ownsHeap = MA_TRUE;
47098 return MA_SUCCESS;
47099}
47100
47101MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks)
47102{
47103 if (pBQ == NULL) {
47104 return;
47105 }
47106
47107 if (pBQ->_ownsHeap) {
47108 ma_free(pBQ->_pHeap, pAllocationCallbacks);
47109 }
47110}
47111
47113{
47114 if (pBQ == NULL || pConfig == NULL) {
47115 return MA_INVALID_ARGS;
47116 }
47117
47118 if (pConfig->a0 == 0) {
47119 return MA_INVALID_ARGS; /* Division by zero. */
47120 }
47121
47122 /* Only supporting f32 and s16. */
47123 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
47124 return MA_INVALID_ARGS;
47125 }
47126
47127 /* The format cannot be changed after initialization. */
47128 if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) {
47129 return MA_INVALID_OPERATION;
47130 }
47131
47132 /* The channel count cannot be changed after initialization. */
47133 if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) {
47134 return MA_INVALID_OPERATION;
47135 }
47136
47137
47138 pBQ->format = pConfig->format;
47139 pBQ->channels = pConfig->channels;
47140
47141 /* Normalize. */
47142 if (pConfig->format == ma_format_f32) {
47143 pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0);
47144 pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0);
47145 pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0);
47146 pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0);
47147 pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0);
47148 } else {
47149 pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0);
47150 pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0);
47151 pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0);
47152 pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0);
47153 pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0);
47154 }
47155
47156 return MA_SUCCESS;
47157}
47158
47160{
47161 if (pBQ == NULL) {
47162 return MA_INVALID_ARGS;
47163 }
47164
47165 if (pBQ->format == ma_format_f32) {
47166 pBQ->pR1->f32 = 0;
47167 pBQ->pR2->f32 = 0;
47168 } else {
47169 pBQ->pR1->s32 = 0;
47170 pBQ->pR2->s32 = 0;
47171 }
47172
47173 return MA_SUCCESS;
47174}
47175
47176static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX)
47177{
47178 ma_uint32 c;
47179 const ma_uint32 channels = pBQ->channels;
47180 const float b0 = pBQ->b0.f32;
47181 const float b1 = pBQ->b1.f32;
47182 const float b2 = pBQ->b2.f32;
47183 const float a1 = pBQ->a1.f32;
47184 const float a2 = pBQ->a2.f32;
47185
47186 MA_ASSUME(channels > 0);
47187 for (c = 0; c < channels; c += 1) {
47188 float r1 = pBQ->pR1[c].f32;
47189 float r2 = pBQ->pR2[c].f32;
47190 float x = pX[c];
47191 float y;
47192
47193 y = b0*x + r1;
47194 r1 = b1*x - a1*y + r2;
47195 r2 = b2*x - a2*y;
47196
47197 pY[c] = y;
47198 pBQ->pR1[c].f32 = r1;
47199 pBQ->pR2[c].f32 = r2;
47200 }
47201}
47202
47203static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX)
47204{
47205 ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
47206}
47207
47208static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
47209{
47210 ma_uint32 c;
47211 const ma_uint32 channels = pBQ->channels;
47212 const ma_int32 b0 = pBQ->b0.s32;
47213 const ma_int32 b1 = pBQ->b1.s32;
47214 const ma_int32 b2 = pBQ->b2.s32;
47215 const ma_int32 a1 = pBQ->a1.s32;
47216 const ma_int32 a2 = pBQ->a2.s32;
47217
47218 MA_ASSUME(channels > 0);
47219 for (c = 0; c < channels; c += 1) {
47220 ma_int32 r1 = pBQ->pR1[c].s32;
47221 ma_int32 r2 = pBQ->pR2[c].s32;
47222 ma_int32 x = pX[c];
47223 ma_int32 y;
47224
47225 y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
47226 r1 = (b1*x - a1*y + r2);
47227 r2 = (b2*x - a2*y);
47228
47229 pY[c] = (ma_int16)ma_clamp(y, -32768, 32767);
47230 pBQ->pR1[c].s32 = r1;
47231 pBQ->pR2[c].s32 = r2;
47232 }
47233}
47234
47235static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
47236{
47237 ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
47238}
47239
47240MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
47241{
47242 ma_uint32 n;
47243
47244 if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) {
47245 return MA_INVALID_ARGS;
47246 }
47247
47248 /* 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. */
47249
47250 if (pBQ->format == ma_format_f32) {
47251 /* */ float* pY = ( float*)pFramesOut;
47252 const float* pX = (const float*)pFramesIn;
47253
47254 for (n = 0; n < frameCount; n += 1) {
47255 ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
47256 pY += pBQ->channels;
47257 pX += pBQ->channels;
47258 }
47259 } else if (pBQ->format == ma_format_s16) {
47260 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
47261 const ma_int16* pX = (const ma_int16*)pFramesIn;
47262
47263 for (n = 0; n < frameCount; n += 1) {
47264 ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
47265 pY += pBQ->channels;
47266 pX += pBQ->channels;
47267 }
47268 } else {
47269 MA_ASSERT(MA_FALSE);
47270 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
47271 }
47272
47273 return MA_SUCCESS;
47274}
47275
47277{
47278 if (pBQ == NULL) {
47279 return 0;
47280 }
47281
47282 return 2;
47283}
47284
47285
47286/**************************************************************************************************************************************************************
47287
47288Low-Pass Filter
47289
47290**************************************************************************************************************************************************************/
47291MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
47292{
47293 ma_lpf1_config config;
47294
47295 MA_ZERO_OBJECT(&config);
47296 config.format = format;
47297 config.channels = channels;
47298 config.sampleRate = sampleRate;
47299 config.cutoffFrequency = cutoffFrequency;
47300 config.q = 0.5;
47301
47302 return config;
47303}
47304
47305MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
47306{
47307 ma_lpf2_config config;
47308
47309 MA_ZERO_OBJECT(&config);
47310 config.format = format;
47311 config.channels = channels;
47312 config.sampleRate = sampleRate;
47313 config.cutoffFrequency = cutoffFrequency;
47314 config.q = q;
47315
47316 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
47317 if (config.q == 0) {
47318 config.q = 0.707107;
47319 }
47320
47321 return config;
47322}
47323
47324
47325typedef struct
47326{
47327 size_t sizeInBytes;
47328 size_t r1Offset;
47329} ma_lpf1_heap_layout;
47330
47331static ma_result ma_lpf1_get_heap_layout(const ma_lpf1_config* pConfig, ma_lpf1_heap_layout* pHeapLayout)
47332{
47333 MA_ASSERT(pHeapLayout != NULL);
47334
47335 MA_ZERO_OBJECT(pHeapLayout);
47336
47337 if (pConfig == NULL) {
47338 return MA_INVALID_ARGS;
47339 }
47340
47341 if (pConfig->channels == 0) {
47342 return MA_INVALID_ARGS;
47343 }
47344
47345 pHeapLayout->sizeInBytes = 0;
47346
47347 /* R1 */
47348 pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;
47349 pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
47350
47351 /* Make sure allocation size is aligned. */
47352 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
47353
47354 return MA_SUCCESS;
47355}
47356
47357MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes)
47358{
47359 ma_result result;
47360 ma_lpf1_heap_layout heapLayout;
47361
47362 if (pHeapSizeInBytes == NULL) {
47363 return MA_INVALID_ARGS;
47364 }
47365
47366 result = ma_lpf1_get_heap_layout(pConfig, &heapLayout);
47367 if (result != MA_SUCCESS) {
47368 return result;
47369 }
47370
47371 *pHeapSizeInBytes = heapLayout.sizeInBytes;
47372
47373 return MA_SUCCESS;
47374}
47375
47376MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF)
47377{
47378 ma_result result;
47379 ma_lpf1_heap_layout heapLayout;
47380
47381 if (pLPF == NULL) {
47382 return MA_INVALID_ARGS;
47383 }
47384
47385 MA_ZERO_OBJECT(pLPF);
47386
47387 result = ma_lpf1_get_heap_layout(pConfig, &heapLayout);
47388 if (result != MA_SUCCESS) {
47389 return result;
47390 }
47391
47392 pLPF->_pHeap = pHeap;
47393 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
47394
47395 pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);
47396
47397 return ma_lpf1_reinit(pConfig, pLPF);
47398}
47399
47400MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF)
47401{
47402 ma_result result;
47403 size_t heapSizeInBytes;
47404 void* pHeap;
47405
47406 result = ma_lpf1_get_heap_size(pConfig, &heapSizeInBytes);
47407 if (result != MA_SUCCESS) {
47408 return result;
47409 }
47410
47411 if (heapSizeInBytes > 0) {
47412 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
47413 if (pHeap == NULL) {
47414 return MA_OUT_OF_MEMORY;
47415 }
47416 } else {
47417 pHeap = NULL;
47418 }
47419
47420 result = ma_lpf1_init_preallocated(pConfig, pHeap, pLPF);
47421 if (result != MA_SUCCESS) {
47422 ma_free(pHeap, pAllocationCallbacks);
47423 return result;
47424 }
47425
47426 pLPF->_ownsHeap = MA_TRUE;
47427 return MA_SUCCESS;
47428}
47429
47430MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)
47431{
47432 if (pLPF == NULL) {
47433 return;
47434 }
47435
47436 if (pLPF->_ownsHeap) {
47437 ma_free(pLPF->_pHeap, pAllocationCallbacks);
47438 }
47439}
47440
47442{
47443 double a;
47444
47445 if (pLPF == NULL || pConfig == NULL) {
47446 return MA_INVALID_ARGS;
47447 }
47448
47449 /* Only supporting f32 and s16. */
47450 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
47451 return MA_INVALID_ARGS;
47452 }
47453
47454 /* The format cannot be changed after initialization. */
47455 if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {
47456 return MA_INVALID_OPERATION;
47457 }
47458
47459 /* The channel count cannot be changed after initialization. */
47460 if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {
47461 return MA_INVALID_OPERATION;
47462 }
47463
47464 pLPF->format = pConfig->format;
47465 pLPF->channels = pConfig->channels;
47466
47467 a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);
47468 if (pConfig->format == ma_format_f32) {
47469 pLPF->a.f32 = (float)a;
47470 } else {
47471 pLPF->a.s32 = ma_biquad_float_to_fp(a);
47472 }
47473
47474 return MA_SUCCESS;
47475}
47476
47478{
47479 if (pLPF == NULL) {
47480 return MA_INVALID_ARGS;
47481 }
47482
47483 if (pLPF->format == ma_format_f32) {
47484 pLPF->a.f32 = 0;
47485 } else {
47486 pLPF->a.s32 = 0;
47487 }
47488
47489 return MA_SUCCESS;
47490}
47491
47492static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX)
47493{
47494 ma_uint32 c;
47495 const ma_uint32 channels = pLPF->channels;
47496 const float a = pLPF->a.f32;
47497 const float b = 1 - a;
47498
47499 MA_ASSUME(channels > 0);
47500 for (c = 0; c < channels; c += 1) {
47501 float r1 = pLPF->pR1[c].f32;
47502 float x = pX[c];
47503 float y;
47504
47505 y = b*x + a*r1;
47506
47507 pY[c] = y;
47508 pLPF->pR1[c].f32 = y;
47509 }
47510}
47511
47512static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX)
47513{
47514 ma_uint32 c;
47515 const ma_uint32 channels = pLPF->channels;
47516 const ma_int32 a = pLPF->a.s32;
47517 const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);
47518
47519 MA_ASSUME(channels > 0);
47520 for (c = 0; c < channels; c += 1) {
47521 ma_int32 r1 = pLPF->pR1[c].s32;
47522 ma_int32 x = pX[c];
47523 ma_int32 y;
47524
47525 y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
47526
47527 pY[c] = (ma_int16)y;
47528 pLPF->pR1[c].s32 = (ma_int32)y;
47529 }
47530}
47531
47532MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
47533{
47534 ma_uint32 n;
47535
47536 if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {
47537 return MA_INVALID_ARGS;
47538 }
47539
47540 /* 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. */
47541
47542 if (pLPF->format == ma_format_f32) {
47543 /* */ float* pY = ( float*)pFramesOut;
47544 const float* pX = (const float*)pFramesIn;
47545
47546 for (n = 0; n < frameCount; n += 1) {
47547 ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX);
47548 pY += pLPF->channels;
47549 pX += pLPF->channels;
47550 }
47551 } else if (pLPF->format == ma_format_s16) {
47552 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
47553 const ma_int16* pX = (const ma_int16*)pFramesIn;
47554
47555 for (n = 0; n < frameCount; n += 1) {
47556 ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX);
47557 pY += pLPF->channels;
47558 pX += pLPF->channels;
47559 }
47560 } else {
47561 MA_ASSERT(MA_FALSE);
47562 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
47563 }
47564
47565 return MA_SUCCESS;
47566}
47567
47569{
47570 if (pLPF == NULL) {
47571 return 0;
47572 }
47573
47574 return 1;
47575}
47576
47577
47578static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig)
47579{
47580 ma_biquad_config bqConfig;
47581 double q;
47582 double w;
47583 double s;
47584 double c;
47585 double a;
47586
47587 MA_ASSERT(pConfig != NULL);
47588
47589 q = pConfig->q;
47590 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
47591 s = ma_sind(w);
47592 c = ma_cosd(w);
47593 a = s / (2*q);
47594
47595 bqConfig.b0 = (1 - c) / 2;
47596 bqConfig.b1 = 1 - c;
47597 bqConfig.b2 = (1 - c) / 2;
47598 bqConfig.a0 = 1 + a;
47599 bqConfig.a1 = -2 * c;
47600 bqConfig.a2 = 1 - a;
47601
47602 bqConfig.format = pConfig->format;
47603 bqConfig.channels = pConfig->channels;
47604
47605 return bqConfig;
47606}
47607
47608MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes)
47609{
47610 ma_biquad_config bqConfig;
47611 bqConfig = ma_lpf2__get_biquad_config(pConfig);
47612
47613 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
47614}
47615
47616MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pLPF)
47617{
47618 ma_result result;
47619 ma_biquad_config bqConfig;
47620
47621 if (pLPF == NULL) {
47622 return MA_INVALID_ARGS;
47623 }
47624
47625 MA_ZERO_OBJECT(pLPF);
47626
47627 if (pConfig == NULL) {
47628 return MA_INVALID_ARGS;
47629 }
47630
47631 bqConfig = ma_lpf2__get_biquad_config(pConfig);
47632 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pLPF->bq);
47633 if (result != MA_SUCCESS) {
47634 return result;
47635 }
47636
47637 return MA_SUCCESS;
47638}
47639
47640MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF)
47641{
47642 ma_result result;
47643 size_t heapSizeInBytes;
47644 void* pHeap;
47645
47646 result = ma_lpf2_get_heap_size(pConfig, &heapSizeInBytes);
47647 if (result != MA_SUCCESS) {
47648 return result;
47649 }
47650
47651 if (heapSizeInBytes > 0) {
47652 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
47653 if (pHeap == NULL) {
47654 return MA_OUT_OF_MEMORY;
47655 }
47656 } else {
47657 pHeap = NULL;
47658 }
47659
47660 result = ma_lpf2_init_preallocated(pConfig, pHeap, pLPF);
47661 if (result != MA_SUCCESS) {
47662 ma_free(pHeap, pAllocationCallbacks);
47663 return result;
47664 }
47665
47666 pLPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
47667 return MA_SUCCESS;
47668}
47669
47670MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)
47671{
47672 if (pLPF == NULL) {
47673 return;
47674 }
47675
47676 ma_biquad_uninit(&pLPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
47677}
47678
47680{
47681 ma_result result;
47682 ma_biquad_config bqConfig;
47683
47684 if (pLPF == NULL || pConfig == NULL) {
47685 return MA_INVALID_ARGS;
47686 }
47687
47688 bqConfig = ma_lpf2__get_biquad_config(pConfig);
47689 result = ma_biquad_reinit(&bqConfig, &pLPF->bq);
47690 if (result != MA_SUCCESS) {
47691 return result;
47692 }
47693
47694 return MA_SUCCESS;
47695}
47696
47698{
47699 if (pLPF == NULL) {
47700 return MA_INVALID_ARGS;
47701 }
47702
47703 ma_biquad_clear_cache(&pLPF->bq);
47704
47705 return MA_SUCCESS;
47706}
47707
47708static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
47709{
47710 ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn);
47711}
47712
47713static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn)
47714{
47715 ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn);
47716}
47717
47718MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
47719{
47720 if (pLPF == NULL) {
47721 return MA_INVALID_ARGS;
47722 }
47723
47724 return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount);
47725}
47726
47728{
47729 if (pLPF == NULL) {
47730 return 0;
47731 }
47732
47733 return ma_biquad_get_latency(&pLPF->bq);
47734}
47735
47736
47737MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
47738{
47739 ma_lpf_config config;
47740
47741 MA_ZERO_OBJECT(&config);
47742 config.format = format;
47743 config.channels = channels;
47744 config.sampleRate = sampleRate;
47745 config.cutoffFrequency = cutoffFrequency;
47746 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
47747
47748 return config;
47749}
47750
47751
47752typedef struct
47753{
47754 size_t sizeInBytes;
47755 size_t lpf1Offset;
47756 size_t lpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */
47757} ma_lpf_heap_layout;
47758
47759static void ma_lpf_calculate_sub_lpf_counts(ma_uint32 order, ma_uint32* pLPF1Count, ma_uint32* pLPF2Count)
47760{
47761 MA_ASSERT(pLPF1Count != NULL);
47762 MA_ASSERT(pLPF2Count != NULL);
47763
47764 *pLPF1Count = order % 2;
47765 *pLPF2Count = order / 2;
47766}
47767
47768static ma_result ma_lpf_get_heap_layout(const ma_lpf_config* pConfig, ma_lpf_heap_layout* pHeapLayout)
47769{
47770 ma_result result;
47771 ma_uint32 lpf1Count;
47772 ma_uint32 lpf2Count;
47773 ma_uint32 ilpf1;
47774 ma_uint32 ilpf2;
47775
47776 MA_ASSERT(pHeapLayout != NULL);
47777
47778 MA_ZERO_OBJECT(pHeapLayout);
47779
47780 if (pConfig == NULL) {
47781 return MA_INVALID_ARGS;
47782 }
47783
47784 if (pConfig->channels == 0) {
47785 return MA_INVALID_ARGS;
47786 }
47787
47788 if (pConfig->order > MA_MAX_FILTER_ORDER) {
47789 return MA_INVALID_ARGS;
47790 }
47791
47792 ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count);
47793
47794 pHeapLayout->sizeInBytes = 0;
47795
47796 /* LPF 1 */
47797 pHeapLayout->lpf1Offset = pHeapLayout->sizeInBytes;
47798 for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) {
47799 size_t lpf1HeapSizeInBytes;
47800 ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
47801
47802 result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes);
47803 if (result != MA_SUCCESS) {
47804 return result;
47805 }
47806
47807 pHeapLayout->sizeInBytes += sizeof(ma_lpf1) + lpf1HeapSizeInBytes;
47808 }
47809
47810 /* LPF 2*/
47811 pHeapLayout->lpf2Offset = pHeapLayout->sizeInBytes;
47812 for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) {
47813 size_t lpf2HeapSizeInBytes;
47814 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. */
47815
47816 result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes);
47817 if (result != MA_SUCCESS) {
47818 return result;
47819 }
47820
47821 pHeapLayout->sizeInBytes += sizeof(ma_lpf2) + lpf2HeapSizeInBytes;
47822 }
47823
47824 /* Make sure allocation size is aligned. */
47825 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
47826
47827 return MA_SUCCESS;
47828}
47829
47830static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF, ma_bool32 isNew)
47831{
47832 ma_result result;
47833 ma_uint32 lpf1Count;
47834 ma_uint32 lpf2Count;
47835 ma_uint32 ilpf1;
47836 ma_uint32 ilpf2;
47837 ma_lpf_heap_layout heapLayout; /* Only used if isNew is true. */
47838
47839 if (pLPF == NULL || pConfig == NULL) {
47840 return MA_INVALID_ARGS;
47841 }
47842
47843 /* Only supporting f32 and s16. */
47844 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
47845 return MA_INVALID_ARGS;
47846 }
47847
47848 /* The format cannot be changed after initialization. */
47849 if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {
47850 return MA_INVALID_OPERATION;
47851 }
47852
47853 /* The channel count cannot be changed after initialization. */
47854 if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {
47855 return MA_INVALID_OPERATION;
47856 }
47857
47858 if (pConfig->order > MA_MAX_FILTER_ORDER) {
47859 return MA_INVALID_ARGS;
47860 }
47861
47862 ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count);
47863
47864 /* The filter order can't change between reinits. */
47865 if (!isNew) {
47866 if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) {
47867 return MA_INVALID_OPERATION;
47868 }
47869 }
47870
47871 if (isNew) {
47872 result = ma_lpf_get_heap_layout(pConfig, &heapLayout);
47873 if (result != MA_SUCCESS) {
47874 return result;
47875 }
47876
47877 pLPF->_pHeap = pHeap;
47878 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
47879
47880 pLPF->pLPF1 = (ma_lpf1*)ma_offset_ptr(pHeap, heapLayout.lpf1Offset);
47881 pLPF->pLPF2 = (ma_lpf2*)ma_offset_ptr(pHeap, heapLayout.lpf2Offset);
47882 } else {
47883 MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */
47884 }
47885
47886 for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) {
47887 ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
47888
47889 if (isNew) {
47890 size_t lpf1HeapSizeInBytes;
47891
47892 result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes);
47893 if (result == MA_SUCCESS) {
47894 result = ma_lpf1_init_preallocated(&lpf1Config, ma_offset_ptr(pHeap, heapLayout.lpf1Offset + (sizeof(ma_lpf1) * lpf1Count) + (ilpf1 * lpf1HeapSizeInBytes)), &pLPF->pLPF1[ilpf1]);
47895 }
47896 } else {
47897 result = ma_lpf1_reinit(&lpf1Config, &pLPF->pLPF1[ilpf1]);
47898 }
47899
47900 if (result != MA_SUCCESS) {
47901 ma_uint32 jlpf1;
47902
47903 for (jlpf1 = 0; jlpf1 < ilpf1; jlpf1 += 1) {
47904 ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
47905 }
47906
47907 return result;
47908 }
47909 }
47910
47911 for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) {
47912 ma_lpf2_config lpf2Config;
47913 double q;
47914 double a;
47915
47916 /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */
47917 if (lpf1Count == 1) {
47918 a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */
47919 } else {
47920 a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */
47921 }
47922 q = 1 / (2*ma_cosd(a));
47923
47924 lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
47925
47926 if (isNew) {
47927 size_t lpf2HeapSizeInBytes;
47928
47929 result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes);
47930 if (result == MA_SUCCESS) {
47931 result = ma_lpf2_init_preallocated(&lpf2Config, ma_offset_ptr(pHeap, heapLayout.lpf2Offset + (sizeof(ma_lpf2) * lpf2Count) + (ilpf2 * lpf2HeapSizeInBytes)), &pLPF->pLPF2[ilpf2]);
47932 }
47933 } else {
47934 result = ma_lpf2_reinit(&lpf2Config, &pLPF->pLPF2[ilpf2]);
47935 }
47936
47937 if (result != MA_SUCCESS) {
47938 ma_uint32 jlpf1;
47939 ma_uint32 jlpf2;
47940
47941 for (jlpf1 = 0; jlpf1 < lpf1Count; jlpf1 += 1) {
47942 ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
47943 }
47944
47945 for (jlpf2 = 0; jlpf2 < ilpf2; jlpf2 += 1) {
47946 ma_lpf2_uninit(&pLPF->pLPF2[jlpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
47947 }
47948
47949 return result;
47950 }
47951 }
47952
47953 pLPF->lpf1Count = lpf1Count;
47954 pLPF->lpf2Count = lpf2Count;
47955 pLPF->format = pConfig->format;
47956 pLPF->channels = pConfig->channels;
47957 pLPF->sampleRate = pConfig->sampleRate;
47958
47959 return MA_SUCCESS;
47960}
47961
47962MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes)
47963{
47964 ma_result result;
47965 ma_lpf_heap_layout heapLayout;
47966
47967 if (pHeapSizeInBytes == NULL) {
47968 return MA_INVALID_ARGS;
47969 }
47970
47971 *pHeapSizeInBytes = 0;
47972
47973 result = ma_lpf_get_heap_layout(pConfig, &heapLayout);
47974 if (result != MA_SUCCESS) {
47975 return result;
47976 }
47977
47978 *pHeapSizeInBytes = heapLayout.sizeInBytes;
47979
47980 return result;
47981}
47982
47983MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF)
47984{
47985 if (pLPF == NULL) {
47986 return MA_INVALID_ARGS;
47987 }
47988
47989 MA_ZERO_OBJECT(pLPF);
47990
47991 return ma_lpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE);
47992}
47993
47994MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF)
47995{
47996 ma_result result;
47997 size_t heapSizeInBytes;
47998 void* pHeap;
47999
48000 result = ma_lpf_get_heap_size(pConfig, &heapSizeInBytes);
48001 if (result != MA_SUCCESS) {
48002 return result;
48003 }
48004
48005 if (heapSizeInBytes > 0) {
48006 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
48007 if (pHeap == NULL) {
48008 return MA_OUT_OF_MEMORY;
48009 }
48010 } else {
48011 pHeap = NULL;
48012 }
48013
48014 result = ma_lpf_init_preallocated(pConfig, pHeap, pLPF);
48015 if (result != MA_SUCCESS) {
48016 ma_free(pHeap, pAllocationCallbacks);
48017 return result;
48018 }
48019
48020 pLPF->_ownsHeap = MA_TRUE;
48021 return MA_SUCCESS;
48022}
48023
48024MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)
48025{
48026 ma_uint32 ilpf1;
48027 ma_uint32 ilpf2;
48028
48029 if (pLPF == NULL) {
48030 return;
48031 }
48032
48033 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
48034 ma_lpf1_uninit(&pLPF->pLPF1[ilpf1], pAllocationCallbacks);
48035 }
48036
48037 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
48038 ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks);
48039 }
48040
48041 if (pLPF->_ownsHeap) {
48042 ma_free(pLPF->_pHeap, pAllocationCallbacks);
48043 }
48044}
48045
48046MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF)
48047{
48048 return ma_lpf_reinit__internal(pConfig, NULL, pLPF, /*isNew*/MA_FALSE);
48049}
48050
48052{
48053 ma_uint32 ilpf1;
48054 ma_uint32 ilpf2;
48055
48056 if (pLPF == NULL) {
48057 return MA_INVALID_ARGS;
48058 }
48059
48060 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
48061 ma_lpf1_clear_cache(&pLPF->pLPF1[ilpf1]);
48062 }
48063
48064 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
48065 ma_lpf2_clear_cache(&pLPF->pLPF2[ilpf2]);
48066 }
48067
48068 return MA_SUCCESS;
48069}
48070
48071static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX)
48072{
48073 ma_uint32 ilpf1;
48074 ma_uint32 ilpf2;
48075
48076 MA_ASSERT(pLPF->format == ma_format_f32);
48077
48078 MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));
48079
48080 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
48081 ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY);
48082 }
48083
48084 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
48085 ma_lpf2_process_pcm_frame_f32(&pLPF->pLPF2[ilpf2], pY, pY);
48086 }
48087}
48088
48089static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX)
48090{
48091 ma_uint32 ilpf1;
48092 ma_uint32 ilpf2;
48093
48094 MA_ASSERT(pLPF->format == ma_format_s16);
48095
48096 MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));
48097
48098 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
48099 ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY);
48100 }
48101
48102 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
48103 ma_lpf2_process_pcm_frame_s16(&pLPF->pLPF2[ilpf2], pY, pY);
48104 }
48105}
48106
48107MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
48108{
48109 ma_result result;
48110 ma_uint32 ilpf1;
48111 ma_uint32 ilpf2;
48112
48113 if (pLPF == NULL) {
48114 return MA_INVALID_ARGS;
48115 }
48116
48117 /* Faster path for in-place. */
48118 if (pFramesOut == pFramesIn) {
48119 for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
48120 result = ma_lpf1_process_pcm_frames(&pLPF->pLPF1[ilpf1], pFramesOut, pFramesOut, frameCount);
48121 if (result != MA_SUCCESS) {
48122 return result;
48123 }
48124 }
48125
48126 for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
48127 result = ma_lpf2_process_pcm_frames(&pLPF->pLPF2[ilpf2], pFramesOut, pFramesOut, frameCount);
48128 if (result != MA_SUCCESS) {
48129 return result;
48130 }
48131 }
48132 }
48133
48134 /* Slightly slower path for copying. */
48135 if (pFramesOut != pFramesIn) {
48136 ma_uint32 iFrame;
48137
48138 /* */ if (pLPF->format == ma_format_f32) {
48139 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
48140 const float* pFramesInF32 = (const float*)pFramesIn;
48141
48142 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48143 ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32);
48144 pFramesOutF32 += pLPF->channels;
48145 pFramesInF32 += pLPF->channels;
48146 }
48147 } else if (pLPF->format == ma_format_s16) {
48148 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
48149 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
48150
48151 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48152 ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16);
48153 pFramesOutS16 += pLPF->channels;
48154 pFramesInS16 += pLPF->channels;
48155 }
48156 } else {
48157 MA_ASSERT(MA_FALSE);
48158 return MA_INVALID_OPERATION; /* Should never hit this. */
48159 }
48160 }
48161
48162 return MA_SUCCESS;
48163}
48164
48166{
48167 if (pLPF == NULL) {
48168 return 0;
48169 }
48170
48171 return pLPF->lpf2Count*2 + pLPF->lpf1Count;
48172}
48173
48174
48175/**************************************************************************************************************************************************************
48176
48177High-Pass Filtering
48178
48179**************************************************************************************************************************************************************/
48180MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
48181{
48182 ma_hpf1_config config;
48183
48184 MA_ZERO_OBJECT(&config);
48185 config.format = format;
48186 config.channels = channels;
48187 config.sampleRate = sampleRate;
48188 config.cutoffFrequency = cutoffFrequency;
48189
48190 return config;
48191}
48192
48193MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
48194{
48195 ma_hpf2_config config;
48196
48197 MA_ZERO_OBJECT(&config);
48198 config.format = format;
48199 config.channels = channels;
48200 config.sampleRate = sampleRate;
48201 config.cutoffFrequency = cutoffFrequency;
48202 config.q = q;
48203
48204 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
48205 if (config.q == 0) {
48206 config.q = 0.707107;
48207 }
48208
48209 return config;
48210}
48211
48212
48213typedef struct
48214{
48215 size_t sizeInBytes;
48216 size_t r1Offset;
48217} ma_hpf1_heap_layout;
48218
48219static ma_result ma_hpf1_get_heap_layout(const ma_hpf1_config* pConfig, ma_hpf1_heap_layout* pHeapLayout)
48220{
48221 MA_ASSERT(pHeapLayout != NULL);
48222
48223 MA_ZERO_OBJECT(pHeapLayout);
48224
48225 if (pConfig == NULL) {
48226 return MA_INVALID_ARGS;
48227 }
48228
48229 if (pConfig->channels == 0) {
48230 return MA_INVALID_ARGS;
48231 }
48232
48233 pHeapLayout->sizeInBytes = 0;
48234
48235 /* R1 */
48236 pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;
48237 pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
48238
48239 /* Make sure allocation size is aligned. */
48240 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
48241
48242 return MA_SUCCESS;
48243}
48244
48245MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes)
48246{
48247 ma_result result;
48248 ma_hpf1_heap_layout heapLayout;
48249
48250 if (pHeapSizeInBytes == NULL) {
48251 return MA_INVALID_ARGS;
48252 }
48253
48254 result = ma_hpf1_get_heap_layout(pConfig, &heapLayout);
48255 if (result != MA_SUCCESS) {
48256 return result;
48257 }
48258
48259 *pHeapSizeInBytes = heapLayout.sizeInBytes;
48260
48261 return MA_SUCCESS;
48262}
48263
48264MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF)
48265{
48266 ma_result result;
48267 ma_hpf1_heap_layout heapLayout;
48268
48269 if (pLPF == NULL) {
48270 return MA_INVALID_ARGS;
48271 }
48272
48273 MA_ZERO_OBJECT(pLPF);
48274
48275 result = ma_hpf1_get_heap_layout(pConfig, &heapLayout);
48276 if (result != MA_SUCCESS) {
48277 return result;
48278 }
48279
48280 pLPF->_pHeap = pHeap;
48281 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
48282
48283 pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);
48284
48285 return ma_hpf1_reinit(pConfig, pLPF);
48286}
48287
48288MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pLPF)
48289{
48290 ma_result result;
48291 size_t heapSizeInBytes;
48292 void* pHeap;
48293
48294 result = ma_hpf1_get_heap_size(pConfig, &heapSizeInBytes);
48295 if (result != MA_SUCCESS) {
48296 return result;
48297 }
48298
48299 if (heapSizeInBytes > 0) {
48300 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
48301 if (pHeap == NULL) {
48302 return MA_OUT_OF_MEMORY;
48303 }
48304 } else {
48305 pHeap = NULL;
48306 }
48307
48308 result = ma_hpf1_init_preallocated(pConfig, pHeap, pLPF);
48309 if (result != MA_SUCCESS) {
48310 ma_free(pHeap, pAllocationCallbacks);
48311 return result;
48312 }
48313
48314 pLPF->_ownsHeap = MA_TRUE;
48315 return MA_SUCCESS;
48316}
48317
48318MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)
48319{
48320 if (pHPF == NULL) {
48321 return;
48322 }
48323
48324 if (pHPF->_ownsHeap) {
48325 ma_free(pHPF->_pHeap, pAllocationCallbacks);
48326 }
48327}
48328
48330{
48331 double a;
48332
48333 if (pHPF == NULL || pConfig == NULL) {
48334 return MA_INVALID_ARGS;
48335 }
48336
48337 /* Only supporting f32 and s16. */
48338 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
48339 return MA_INVALID_ARGS;
48340 }
48341
48342 /* The format cannot be changed after initialization. */
48343 if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {
48344 return MA_INVALID_OPERATION;
48345 }
48346
48347 /* The channel count cannot be changed after initialization. */
48348 if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {
48349 return MA_INVALID_OPERATION;
48350 }
48351
48352 pHPF->format = pConfig->format;
48353 pHPF->channels = pConfig->channels;
48354
48355 a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);
48356 if (pConfig->format == ma_format_f32) {
48357 pHPF->a.f32 = (float)a;
48358 } else {
48359 pHPF->a.s32 = ma_biquad_float_to_fp(a);
48360 }
48361
48362 return MA_SUCCESS;
48363}
48364
48365static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX)
48366{
48367 ma_uint32 c;
48368 const ma_uint32 channels = pHPF->channels;
48369 const float a = 1 - pHPF->a.f32;
48370 const float b = 1 - a;
48371
48372 MA_ASSUME(channels > 0);
48373 for (c = 0; c < channels; c += 1) {
48374 float r1 = pHPF->pR1[c].f32;
48375 float x = pX[c];
48376 float y;
48377
48378 y = b*x - a*r1;
48379
48380 pY[c] = y;
48381 pHPF->pR1[c].f32 = y;
48382 }
48383}
48384
48385static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX)
48386{
48387 ma_uint32 c;
48388 const ma_uint32 channels = pHPF->channels;
48389 const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32);
48390 const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);
48391
48392 MA_ASSUME(channels > 0);
48393 for (c = 0; c < channels; c += 1) {
48394 ma_int32 r1 = pHPF->pR1[c].s32;
48395 ma_int32 x = pX[c];
48396 ma_int32 y;
48397
48398 y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
48399
48400 pY[c] = (ma_int16)y;
48401 pHPF->pR1[c].s32 = (ma_int32)y;
48402 }
48403}
48404
48405MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
48406{
48407 ma_uint32 n;
48408
48409 if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {
48410 return MA_INVALID_ARGS;
48411 }
48412
48413 /* 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. */
48414
48415 if (pHPF->format == ma_format_f32) {
48416 /* */ float* pY = ( float*)pFramesOut;
48417 const float* pX = (const float*)pFramesIn;
48418
48419 for (n = 0; n < frameCount; n += 1) {
48420 ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX);
48421 pY += pHPF->channels;
48422 pX += pHPF->channels;
48423 }
48424 } else if (pHPF->format == ma_format_s16) {
48425 /* */ ma_int16* pY = ( ma_int16*)pFramesOut;
48426 const ma_int16* pX = (const ma_int16*)pFramesIn;
48427
48428 for (n = 0; n < frameCount; n += 1) {
48429 ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX);
48430 pY += pHPF->channels;
48431 pX += pHPF->channels;
48432 }
48433 } else {
48434 MA_ASSERT(MA_FALSE);
48435 return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */
48436 }
48437
48438 return MA_SUCCESS;
48439}
48440
48442{
48443 if (pHPF == NULL) {
48444 return 0;
48445 }
48446
48447 return 1;
48448}
48449
48450
48451static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig)
48452{
48453 ma_biquad_config bqConfig;
48454 double q;
48455 double w;
48456 double s;
48457 double c;
48458 double a;
48459
48460 MA_ASSERT(pConfig != NULL);
48461
48462 q = pConfig->q;
48463 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
48464 s = ma_sind(w);
48465 c = ma_cosd(w);
48466 a = s / (2*q);
48467
48468 bqConfig.b0 = (1 + c) / 2;
48469 bqConfig.b1 = -(1 + c);
48470 bqConfig.b2 = (1 + c) / 2;
48471 bqConfig.a0 = 1 + a;
48472 bqConfig.a1 = -2 * c;
48473 bqConfig.a2 = 1 - a;
48474
48475 bqConfig.format = pConfig->format;
48476 bqConfig.channels = pConfig->channels;
48477
48478 return bqConfig;
48479}
48480
48481MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes)
48482{
48483 ma_biquad_config bqConfig;
48484 bqConfig = ma_hpf2__get_biquad_config(pConfig);
48485
48486 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
48487}
48488
48489MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF)
48490{
48491 ma_result result;
48492 ma_biquad_config bqConfig;
48493
48494 if (pHPF == NULL) {
48495 return MA_INVALID_ARGS;
48496 }
48497
48498 MA_ZERO_OBJECT(pHPF);
48499
48500 if (pConfig == NULL) {
48501 return MA_INVALID_ARGS;
48502 }
48503
48504 bqConfig = ma_hpf2__get_biquad_config(pConfig);
48505 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pHPF->bq);
48506 if (result != MA_SUCCESS) {
48507 return result;
48508 }
48509
48510 return MA_SUCCESS;
48511}
48512
48513MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF)
48514{
48515 ma_result result;
48516 size_t heapSizeInBytes;
48517 void* pHeap;
48518
48519 result = ma_hpf2_get_heap_size(pConfig, &heapSizeInBytes);
48520 if (result != MA_SUCCESS) {
48521 return result;
48522 }
48523
48524 if (heapSizeInBytes > 0) {
48525 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
48526 if (pHeap == NULL) {
48527 return MA_OUT_OF_MEMORY;
48528 }
48529 } else {
48530 pHeap = NULL;
48531 }
48532
48533 result = ma_hpf2_init_preallocated(pConfig, pHeap, pHPF);
48534 if (result != MA_SUCCESS) {
48535 ma_free(pHeap, pAllocationCallbacks);
48536 return result;
48537 }
48538
48539 pHPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
48540 return MA_SUCCESS;
48541}
48542
48543MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)
48544{
48545 if (pHPF == NULL) {
48546 return;
48547 }
48548
48549 ma_biquad_uninit(&pHPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
48550}
48551
48553{
48554 ma_result result;
48555 ma_biquad_config bqConfig;
48556
48557 if (pHPF == NULL || pConfig == NULL) {
48558 return MA_INVALID_ARGS;
48559 }
48560
48561 bqConfig = ma_hpf2__get_biquad_config(pConfig);
48562 result = ma_biquad_reinit(&bqConfig, &pHPF->bq);
48563 if (result != MA_SUCCESS) {
48564 return result;
48565 }
48566
48567 return MA_SUCCESS;
48568}
48569
48570static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
48571{
48572 ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn);
48573}
48574
48575static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn)
48576{
48577 ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn);
48578}
48579
48580MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
48581{
48582 if (pHPF == NULL) {
48583 return MA_INVALID_ARGS;
48584 }
48585
48586 return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount);
48587}
48588
48590{
48591 if (pHPF == NULL) {
48592 return 0;
48593 }
48594
48595 return ma_biquad_get_latency(&pHPF->bq);
48596}
48597
48598
48599MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
48600{
48601 ma_hpf_config config;
48602
48603 MA_ZERO_OBJECT(&config);
48604 config.format = format;
48605 config.channels = channels;
48606 config.sampleRate = sampleRate;
48607 config.cutoffFrequency = cutoffFrequency;
48608 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
48609
48610 return config;
48611}
48612
48613
48614typedef struct
48615{
48616 size_t sizeInBytes;
48617 size_t hpf1Offset;
48618 size_t hpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */
48619} ma_hpf_heap_layout;
48620
48621static void ma_hpf_calculate_sub_hpf_counts(ma_uint32 order, ma_uint32* pHPF1Count, ma_uint32* pHPF2Count)
48622{
48623 MA_ASSERT(pHPF1Count != NULL);
48624 MA_ASSERT(pHPF2Count != NULL);
48625
48626 *pHPF1Count = order % 2;
48627 *pHPF2Count = order / 2;
48628}
48629
48630static ma_result ma_hpf_get_heap_layout(const ma_hpf_config* pConfig, ma_hpf_heap_layout* pHeapLayout)
48631{
48632 ma_result result;
48633 ma_uint32 hpf1Count;
48634 ma_uint32 hpf2Count;
48635 ma_uint32 ihpf1;
48636 ma_uint32 ihpf2;
48637
48638 MA_ASSERT(pHeapLayout != NULL);
48639
48640 MA_ZERO_OBJECT(pHeapLayout);
48641
48642 if (pConfig == NULL) {
48643 return MA_INVALID_ARGS;
48644 }
48645
48646 if (pConfig->channels == 0) {
48647 return MA_INVALID_ARGS;
48648 }
48649
48650 if (pConfig->order > MA_MAX_FILTER_ORDER) {
48651 return MA_INVALID_ARGS;
48652 }
48653
48654 ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count);
48655
48656 pHeapLayout->sizeInBytes = 0;
48657
48658 /* HPF 1 */
48659 pHeapLayout->hpf1Offset = pHeapLayout->sizeInBytes;
48660 for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) {
48661 size_t hpf1HeapSizeInBytes;
48662 ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
48663
48664 result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes);
48665 if (result != MA_SUCCESS) {
48666 return result;
48667 }
48668
48669 pHeapLayout->sizeInBytes += sizeof(ma_hpf1) + hpf1HeapSizeInBytes;
48670 }
48671
48672 /* HPF 2*/
48673 pHeapLayout->hpf2Offset = pHeapLayout->sizeInBytes;
48674 for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) {
48675 size_t hpf2HeapSizeInBytes;
48676 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. */
48677
48678 result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes);
48679 if (result != MA_SUCCESS) {
48680 return result;
48681 }
48682
48683 pHeapLayout->sizeInBytes += sizeof(ma_hpf2) + hpf2HeapSizeInBytes;
48684 }
48685
48686 /* Make sure allocation size is aligned. */
48687 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
48688
48689 return MA_SUCCESS;
48690}
48691
48692static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pHPF, ma_bool32 isNew)
48693{
48694 ma_result result;
48695 ma_uint32 hpf1Count;
48696 ma_uint32 hpf2Count;
48697 ma_uint32 ihpf1;
48698 ma_uint32 ihpf2;
48699 ma_hpf_heap_layout heapLayout; /* Only used if isNew is true. */
48700
48701 if (pHPF == NULL || pConfig == NULL) {
48702 return MA_INVALID_ARGS;
48703 }
48704
48705 /* Only supporting f32 and s16. */
48706 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
48707 return MA_INVALID_ARGS;
48708 }
48709
48710 /* The format cannot be changed after initialization. */
48711 if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {
48712 return MA_INVALID_OPERATION;
48713 }
48714
48715 /* The channel count cannot be changed after initialization. */
48716 if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {
48717 return MA_INVALID_OPERATION;
48718 }
48719
48720 if (pConfig->order > MA_MAX_FILTER_ORDER) {
48721 return MA_INVALID_ARGS;
48722 }
48723
48724 ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count);
48725
48726 /* The filter order can't change between reinits. */
48727 if (!isNew) {
48728 if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) {
48729 return MA_INVALID_OPERATION;
48730 }
48731 }
48732
48733 if (isNew) {
48734 result = ma_hpf_get_heap_layout(pConfig, &heapLayout);
48735 if (result != MA_SUCCESS) {
48736 return result;
48737 }
48738
48739 pHPF->_pHeap = pHeap;
48740 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
48741
48742 pHPF->pHPF1 = (ma_hpf1*)ma_offset_ptr(pHeap, heapLayout.hpf1Offset);
48743 pHPF->pHPF2 = (ma_hpf2*)ma_offset_ptr(pHeap, heapLayout.hpf2Offset);
48744 } else {
48745 MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */
48746 }
48747
48748 for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) {
48749 ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
48750
48751 if (isNew) {
48752 size_t hpf1HeapSizeInBytes;
48753
48754 result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes);
48755 if (result == MA_SUCCESS) {
48756 result = ma_hpf1_init_preallocated(&hpf1Config, ma_offset_ptr(pHeap, heapLayout.hpf1Offset + (sizeof(ma_hpf1) * hpf1Count) + (ihpf1 * hpf1HeapSizeInBytes)), &pHPF->pHPF1[ihpf1]);
48757 }
48758 } else {
48759 result = ma_hpf1_reinit(&hpf1Config, &pHPF->pHPF1[ihpf1]);
48760 }
48761
48762 if (result != MA_SUCCESS) {
48763 ma_uint32 jhpf1;
48764
48765 for (jhpf1 = 0; jhpf1 < ihpf1; jhpf1 += 1) {
48766 ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
48767 }
48768
48769 return result;
48770 }
48771 }
48772
48773 for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) {
48774 ma_hpf2_config hpf2Config;
48775 double q;
48776 double a;
48777
48778 /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */
48779 if (hpf1Count == 1) {
48780 a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */
48781 } else {
48782 a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */
48783 }
48784 q = 1 / (2*ma_cosd(a));
48785
48786 hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
48787
48788 if (isNew) {
48789 size_t hpf2HeapSizeInBytes;
48790
48791 result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes);
48792 if (result == MA_SUCCESS) {
48793 result = ma_hpf2_init_preallocated(&hpf2Config, ma_offset_ptr(pHeap, heapLayout.hpf2Offset + (sizeof(ma_hpf2) * hpf2Count) + (ihpf2 * hpf2HeapSizeInBytes)), &pHPF->pHPF2[ihpf2]);
48794 }
48795 } else {
48796 result = ma_hpf2_reinit(&hpf2Config, &pHPF->pHPF2[ihpf2]);
48797 }
48798
48799 if (result != MA_SUCCESS) {
48800 ma_uint32 jhpf1;
48801 ma_uint32 jhpf2;
48802
48803 for (jhpf1 = 0; jhpf1 < hpf1Count; jhpf1 += 1) {
48804 ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
48805 }
48806
48807 for (jhpf2 = 0; jhpf2 < ihpf2; jhpf2 += 1) {
48808 ma_hpf2_uninit(&pHPF->pHPF2[jhpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */
48809 }
48810
48811 return result;
48812 }
48813 }
48814
48815 pHPF->hpf1Count = hpf1Count;
48816 pHPF->hpf2Count = hpf2Count;
48817 pHPF->format = pConfig->format;
48818 pHPF->channels = pConfig->channels;
48819 pHPF->sampleRate = pConfig->sampleRate;
48820
48821 return MA_SUCCESS;
48822}
48823
48824MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes)
48825{
48826 ma_result result;
48827 ma_hpf_heap_layout heapLayout;
48828
48829 if (pHeapSizeInBytes == NULL) {
48830 return MA_INVALID_ARGS;
48831 }
48832
48833 *pHeapSizeInBytes = 0;
48834
48835 result = ma_hpf_get_heap_layout(pConfig, &heapLayout);
48836 if (result != MA_SUCCESS) {
48837 return result;
48838 }
48839
48840 *pHeapSizeInBytes = heapLayout.sizeInBytes;
48841
48842 return result;
48843}
48844
48845MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF)
48846{
48847 if (pLPF == NULL) {
48848 return MA_INVALID_ARGS;
48849 }
48850
48851 MA_ZERO_OBJECT(pLPF);
48852
48853 return ma_hpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE);
48854}
48855
48856MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF)
48857{
48858 ma_result result;
48859 size_t heapSizeInBytes;
48860 void* pHeap;
48861
48862 result = ma_hpf_get_heap_size(pConfig, &heapSizeInBytes);
48863 if (result != MA_SUCCESS) {
48864 return result;
48865 }
48866
48867 if (heapSizeInBytes > 0) {
48868 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
48869 if (pHeap == NULL) {
48870 return MA_OUT_OF_MEMORY;
48871 }
48872 } else {
48873 pHeap = NULL;
48874 }
48875
48876 result = ma_hpf_init_preallocated(pConfig, pHeap, pHPF);
48877 if (result != MA_SUCCESS) {
48878 ma_free(pHeap, pAllocationCallbacks);
48879 return result;
48880 }
48881
48882 pHPF->_ownsHeap = MA_TRUE;
48883 return MA_SUCCESS;
48884}
48885
48886MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)
48887{
48888 ma_uint32 ihpf1;
48889 ma_uint32 ihpf2;
48890
48891 if (pHPF == NULL) {
48892 return;
48893 }
48894
48895 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
48896 ma_hpf1_uninit(&pHPF->pHPF1[ihpf1], pAllocationCallbacks);
48897 }
48898
48899 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
48900 ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks);
48901 }
48902
48903 if (pHPF->_ownsHeap) {
48904 ma_free(pHPF->_pHeap, pAllocationCallbacks);
48905 }
48906}
48907
48908MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF)
48909{
48910 return ma_hpf_reinit__internal(pConfig, NULL, pHPF, /*isNew*/MA_FALSE);
48911}
48912
48913MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
48914{
48915 ma_result result;
48916 ma_uint32 ihpf1;
48917 ma_uint32 ihpf2;
48918
48919 if (pHPF == NULL) {
48920 return MA_INVALID_ARGS;
48921 }
48922
48923 /* Faster path for in-place. */
48924 if (pFramesOut == pFramesIn) {
48925 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
48926 result = ma_hpf1_process_pcm_frames(&pHPF->pHPF1[ihpf1], pFramesOut, pFramesOut, frameCount);
48927 if (result != MA_SUCCESS) {
48928 return result;
48929 }
48930 }
48931
48932 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
48933 result = ma_hpf2_process_pcm_frames(&pHPF->pHPF2[ihpf2], pFramesOut, pFramesOut, frameCount);
48934 if (result != MA_SUCCESS) {
48935 return result;
48936 }
48937 }
48938 }
48939
48940 /* Slightly slower path for copying. */
48941 if (pFramesOut != pFramesIn) {
48942 ma_uint32 iFrame;
48943
48944 /* */ if (pHPF->format == ma_format_f32) {
48945 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
48946 const float* pFramesInF32 = (const float*)pFramesIn;
48947
48948 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48949 MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));
48950
48951 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
48952 ma_hpf1_process_pcm_frame_f32(&pHPF->pHPF1[ihpf1], pFramesOutF32, pFramesOutF32);
48953 }
48954
48955 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
48956 ma_hpf2_process_pcm_frame_f32(&pHPF->pHPF2[ihpf2], pFramesOutF32, pFramesOutF32);
48957 }
48958
48959 pFramesOutF32 += pHPF->channels;
48960 pFramesInF32 += pHPF->channels;
48961 }
48962 } else if (pHPF->format == ma_format_s16) {
48963 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
48964 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
48965
48966 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
48967 MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));
48968
48969 for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
48970 ma_hpf1_process_pcm_frame_s16(&pHPF->pHPF1[ihpf1], pFramesOutS16, pFramesOutS16);
48971 }
48972
48973 for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
48974 ma_hpf2_process_pcm_frame_s16(&pHPF->pHPF2[ihpf2], pFramesOutS16, pFramesOutS16);
48975 }
48976
48977 pFramesOutS16 += pHPF->channels;
48978 pFramesInS16 += pHPF->channels;
48979 }
48980 } else {
48981 MA_ASSERT(MA_FALSE);
48982 return MA_INVALID_OPERATION; /* Should never hit this. */
48983 }
48984 }
48985
48986 return MA_SUCCESS;
48987}
48988
48990{
48991 if (pHPF == NULL) {
48992 return 0;
48993 }
48994
48995 return pHPF->hpf2Count*2 + pHPF->hpf1Count;
48996}
48997
48998
48999/**************************************************************************************************************************************************************
49000
49001Band-Pass Filtering
49002
49003**************************************************************************************************************************************************************/
49004MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
49005{
49006 ma_bpf2_config config;
49007
49008 MA_ZERO_OBJECT(&config);
49009 config.format = format;
49010 config.channels = channels;
49011 config.sampleRate = sampleRate;
49012 config.cutoffFrequency = cutoffFrequency;
49013 config.q = q;
49014
49015 /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */
49016 if (config.q == 0) {
49017 config.q = 0.707107;
49018 }
49019
49020 return config;
49021}
49022
49023
49024static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig)
49025{
49026 ma_biquad_config bqConfig;
49027 double q;
49028 double w;
49029 double s;
49030 double c;
49031 double a;
49032
49033 MA_ASSERT(pConfig != NULL);
49034
49035 q = pConfig->q;
49036 w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
49037 s = ma_sind(w);
49038 c = ma_cosd(w);
49039 a = s / (2*q);
49040
49041 bqConfig.b0 = q * a;
49042 bqConfig.b1 = 0;
49043 bqConfig.b2 = -q * a;
49044 bqConfig.a0 = 1 + a;
49045 bqConfig.a1 = -2 * c;
49046 bqConfig.a2 = 1 - a;
49047
49048 bqConfig.format = pConfig->format;
49049 bqConfig.channels = pConfig->channels;
49050
49051 return bqConfig;
49052}
49053
49054MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes)
49055{
49056 ma_biquad_config bqConfig;
49057 bqConfig = ma_bpf2__get_biquad_config(pConfig);
49058
49059 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
49060}
49061
49062MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF)
49063{
49064 ma_result result;
49065 ma_biquad_config bqConfig;
49066
49067 if (pBPF == NULL) {
49068 return MA_INVALID_ARGS;
49069 }
49070
49071 MA_ZERO_OBJECT(pBPF);
49072
49073 if (pConfig == NULL) {
49074 return MA_INVALID_ARGS;
49075 }
49076
49077 bqConfig = ma_bpf2__get_biquad_config(pConfig);
49078 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pBPF->bq);
49079 if (result != MA_SUCCESS) {
49080 return result;
49081 }
49082
49083 return MA_SUCCESS;
49084}
49085
49086MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF)
49087{
49088 ma_result result;
49089 size_t heapSizeInBytes;
49090 void* pHeap;
49091
49092 result = ma_bpf2_get_heap_size(pConfig, &heapSizeInBytes);
49093 if (result != MA_SUCCESS) {
49094 return result;
49095 }
49096
49097 if (heapSizeInBytes > 0) {
49098 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
49099 if (pHeap == NULL) {
49100 return MA_OUT_OF_MEMORY;
49101 }
49102 } else {
49103 pHeap = NULL;
49104 }
49105
49106 result = ma_bpf2_init_preallocated(pConfig, pHeap, pBPF);
49107 if (result != MA_SUCCESS) {
49108 ma_free(pHeap, pAllocationCallbacks);
49109 return result;
49110 }
49111
49112 pBPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
49113 return MA_SUCCESS;
49114}
49115
49116MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks)
49117{
49118 if (pBPF == NULL) {
49119 return;
49120 }
49121
49122 ma_biquad_uninit(&pBPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
49123}
49124
49126{
49127 ma_result result;
49128 ma_biquad_config bqConfig;
49129
49130 if (pBPF == NULL || pConfig == NULL) {
49131 return MA_INVALID_ARGS;
49132 }
49133
49134 bqConfig = ma_bpf2__get_biquad_config(pConfig);
49135 result = ma_biquad_reinit(&bqConfig, &pBPF->bq);
49136 if (result != MA_SUCCESS) {
49137 return result;
49138 }
49139
49140 return MA_SUCCESS;
49141}
49142
49143static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
49144{
49145 ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn);
49146}
49147
49148static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn)
49149{
49150 ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn);
49151}
49152
49153MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
49154{
49155 if (pBPF == NULL) {
49156 return MA_INVALID_ARGS;
49157 }
49158
49159 return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount);
49160}
49161
49163{
49164 if (pBPF == NULL) {
49165 return 0;
49166 }
49167
49168 return ma_biquad_get_latency(&pBPF->bq);
49169}
49170
49171
49172MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
49173{
49174 ma_bpf_config config;
49175
49176 MA_ZERO_OBJECT(&config);
49177 config.format = format;
49178 config.channels = channels;
49179 config.sampleRate = sampleRate;
49180 config.cutoffFrequency = cutoffFrequency;
49181 config.order = ma_min(order, MA_MAX_FILTER_ORDER);
49182
49183 return config;
49184}
49185
49186
49187typedef struct
49188{
49189 size_t sizeInBytes;
49190 size_t bpf2Offset;
49191} ma_bpf_heap_layout;
49192
49193static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_heap_layout* pHeapLayout)
49194{
49195 ma_result result;
49196 ma_uint32 bpf2Count;
49197 ma_uint32 ibpf2;
49198
49199 MA_ASSERT(pHeapLayout != NULL);
49200
49201 MA_ZERO_OBJECT(pHeapLayout);
49202
49203 if (pConfig == NULL) {
49204 return MA_INVALID_ARGS;
49205 }
49206
49207 if (pConfig->order > MA_MAX_FILTER_ORDER) {
49208 return MA_INVALID_ARGS;
49209 }
49210
49211 /* We must have an even number of order. */
49212 if ((pConfig->order & 0x1) != 0) {
49213 return MA_INVALID_ARGS;
49214 }
49215
49216 bpf2Count = pConfig->order / 2;
49217
49218 pHeapLayout->sizeInBytes = 0;
49219
49220 /* BPF 2 */
49221 pHeapLayout->bpf2Offset = pHeapLayout->sizeInBytes;
49222 for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) {
49223 size_t bpf2HeapSizeInBytes;
49224 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. */
49225
49226 result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes);
49227 if (result != MA_SUCCESS) {
49228 return result;
49229 }
49230
49231 pHeapLayout->sizeInBytes += sizeof(ma_bpf2) + bpf2HeapSizeInBytes;
49232 }
49233
49234 /* Make sure allocation size is aligned. */
49235 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
49236
49237 return MA_SUCCESS;
49238}
49239
49240static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF, ma_bool32 isNew)
49241{
49242 ma_result result;
49243 ma_uint32 bpf2Count;
49244 ma_uint32 ibpf2;
49245 ma_bpf_heap_layout heapLayout; /* Only used if isNew is true. */
49246
49247 if (pBPF == NULL || pConfig == NULL) {
49248 return MA_INVALID_ARGS;
49249 }
49250
49251 /* Only supporting f32 and s16. */
49252 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
49253 return MA_INVALID_ARGS;
49254 }
49255
49256 /* The format cannot be changed after initialization. */
49257 if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) {
49258 return MA_INVALID_OPERATION;
49259 }
49260
49261 /* The channel count cannot be changed after initialization. */
49262 if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) {
49263 return MA_INVALID_OPERATION;
49264 }
49265
49266 if (pConfig->order > MA_MAX_FILTER_ORDER) {
49267 return MA_INVALID_ARGS;
49268 }
49269
49270 /* We must have an even number of order. */
49271 if ((pConfig->order & 0x1) != 0) {
49272 return MA_INVALID_ARGS;
49273 }
49274
49275 bpf2Count = pConfig->order / 2;
49276
49277 /* The filter order can't change between reinits. */
49278 if (!isNew) {
49279 if (pBPF->bpf2Count != bpf2Count) {
49280 return MA_INVALID_OPERATION;
49281 }
49282 }
49283
49284 if (isNew) {
49285 result = ma_bpf_get_heap_layout(pConfig, &heapLayout);
49286 if (result != MA_SUCCESS) {
49287 return result;
49288 }
49289
49290 pBPF->_pHeap = pHeap;
49291 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
49292
49293 pBPF->pBPF2 = (ma_bpf2*)ma_offset_ptr(pHeap, heapLayout.bpf2Offset);
49294 } else {
49295 MA_ZERO_OBJECT(&heapLayout);
49296 }
49297
49298 for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) {
49299 ma_bpf2_config bpf2Config;
49300 double q;
49301
49302 /* TODO: Calculate Q to make this a proper Butterworth filter. */
49303 q = 0.707107;
49304
49305 bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
49306
49307 if (isNew) {
49308 size_t bpf2HeapSizeInBytes;
49309
49310 result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes);
49311 if (result == MA_SUCCESS) {
49312 result = ma_bpf2_init_preallocated(&bpf2Config, ma_offset_ptr(pHeap, heapLayout.bpf2Offset + (sizeof(ma_bpf2) * bpf2Count) + (ibpf2 * bpf2HeapSizeInBytes)), &pBPF->pBPF2[ibpf2]);
49313 }
49314 } else {
49315 result = ma_bpf2_reinit(&bpf2Config, &pBPF->pBPF2[ibpf2]);
49316 }
49317
49318 if (result != MA_SUCCESS) {
49319 return result;
49320 }
49321 }
49322
49323 pBPF->bpf2Count = bpf2Count;
49324 pBPF->format = pConfig->format;
49325 pBPF->channels = pConfig->channels;
49326
49327 return MA_SUCCESS;
49328}
49329
49330
49331MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes)
49332{
49333 ma_result result;
49334 ma_bpf_heap_layout heapLayout;
49335
49336 if (pHeapSizeInBytes == NULL) {
49337 return MA_INVALID_ARGS;
49338 }
49339
49340 *pHeapSizeInBytes = 0;
49341
49342 result = ma_bpf_get_heap_layout(pConfig, &heapLayout);
49343 if (result != MA_SUCCESS) {
49344 return result;
49345 }
49346
49347 *pHeapSizeInBytes = heapLayout.sizeInBytes;
49348
49349 return MA_SUCCESS;
49350}
49351
49352MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF)
49353{
49354 if (pBPF == NULL) {
49355 return MA_INVALID_ARGS;
49356 }
49357
49358 MA_ZERO_OBJECT(pBPF);
49359
49360 return ma_bpf_reinit__internal(pConfig, pHeap, pBPF, /*isNew*/MA_TRUE);
49361}
49362
49363MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF)
49364{
49365 ma_result result;
49366 size_t heapSizeInBytes;
49367 void* pHeap;
49368
49369 result = ma_bpf_get_heap_size(pConfig, &heapSizeInBytes);
49370 if (result != MA_SUCCESS) {
49371 return result;
49372 }
49373
49374 if (heapSizeInBytes > 0) {
49375 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
49376 if (pHeap == NULL) {
49377 return MA_OUT_OF_MEMORY;
49378 }
49379 } else {
49380 pHeap = NULL;
49381 }
49382
49383 result = ma_bpf_init_preallocated(pConfig, pHeap, pBPF);
49384 if (result != MA_SUCCESS) {
49385 ma_free(pHeap, pAllocationCallbacks);
49386 return result;
49387 }
49388
49389 pBPF->_ownsHeap = MA_TRUE;
49390 return MA_SUCCESS;
49391}
49392
49393MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks)
49394{
49395 ma_uint32 ibpf2;
49396
49397 if (pBPF == NULL) {
49398 return;
49399 }
49400
49401 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
49402 ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks);
49403 }
49404
49405 if (pBPF->_ownsHeap) {
49406 ma_free(pBPF->_pHeap, pAllocationCallbacks);
49407 }
49408}
49409
49410MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF)
49411{
49412 return ma_bpf_reinit__internal(pConfig, NULL, pBPF, /*isNew*/MA_FALSE);
49413}
49414
49415MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
49416{
49417 ma_result result;
49418 ma_uint32 ibpf2;
49419
49420 if (pBPF == NULL) {
49421 return MA_INVALID_ARGS;
49422 }
49423
49424 /* Faster path for in-place. */
49425 if (pFramesOut == pFramesIn) {
49426 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
49427 result = ma_bpf2_process_pcm_frames(&pBPF->pBPF2[ibpf2], pFramesOut, pFramesOut, frameCount);
49428 if (result != MA_SUCCESS) {
49429 return result;
49430 }
49431 }
49432 }
49433
49434 /* Slightly slower path for copying. */
49435 if (pFramesOut != pFramesIn) {
49436 ma_uint32 iFrame;
49437
49438 /* */ if (pBPF->format == ma_format_f32) {
49439 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
49440 const float* pFramesInF32 = (const float*)pFramesIn;
49441
49442 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
49443 MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));
49444
49445 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
49446 ma_bpf2_process_pcm_frame_f32(&pBPF->pBPF2[ibpf2], pFramesOutF32, pFramesOutF32);
49447 }
49448
49449 pFramesOutF32 += pBPF->channels;
49450 pFramesInF32 += pBPF->channels;
49451 }
49452 } else if (pBPF->format == ma_format_s16) {
49453 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
49454 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
49455
49456 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
49457 MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));
49458
49459 for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
49460 ma_bpf2_process_pcm_frame_s16(&pBPF->pBPF2[ibpf2], pFramesOutS16, pFramesOutS16);
49461 }
49462
49463 pFramesOutS16 += pBPF->channels;
49464 pFramesInS16 += pBPF->channels;
49465 }
49466 } else {
49467 MA_ASSERT(MA_FALSE);
49468 return MA_INVALID_OPERATION; /* Should never hit this. */
49469 }
49470 }
49471
49472 return MA_SUCCESS;
49473}
49474
49476{
49477 if (pBPF == NULL) {
49478 return 0;
49479 }
49480
49481 return pBPF->bpf2Count*2;
49482}
49483
49484
49485/**************************************************************************************************************************************************************
49486
49487Notching Filter
49488
49489**************************************************************************************************************************************************************/
49490MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
49491{
49492 ma_notch2_config config;
49493
49494 MA_ZERO_OBJECT(&config);
49495 config.format = format;
49496 config.channels = channels;
49497 config.sampleRate = sampleRate;
49498 config.q = q;
49499 config.frequency = frequency;
49500
49501 if (config.q == 0) {
49502 config.q = 0.707107;
49503 }
49504
49505 return config;
49506}
49507
49508
49509static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig)
49510{
49511 ma_biquad_config bqConfig;
49512 double q;
49513 double w;
49514 double s;
49515 double c;
49516 double a;
49517
49518 MA_ASSERT(pConfig != NULL);
49519
49520 q = pConfig->q;
49521 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
49522 s = ma_sind(w);
49523 c = ma_cosd(w);
49524 a = s / (2*q);
49525
49526 bqConfig.b0 = 1;
49527 bqConfig.b1 = -2 * c;
49528 bqConfig.b2 = 1;
49529 bqConfig.a0 = 1 + a;
49530 bqConfig.a1 = -2 * c;
49531 bqConfig.a2 = 1 - a;
49532
49533 bqConfig.format = pConfig->format;
49534 bqConfig.channels = pConfig->channels;
49535
49536 return bqConfig;
49537}
49538
49539MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes)
49540{
49541 ma_biquad_config bqConfig;
49542 bqConfig = ma_notch2__get_biquad_config(pConfig);
49543
49544 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
49545}
49546
49547MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter)
49548{
49549 ma_result result;
49550 ma_biquad_config bqConfig;
49551
49552 if (pFilter == NULL) {
49553 return MA_INVALID_ARGS;
49554 }
49555
49556 MA_ZERO_OBJECT(pFilter);
49557
49558 if (pConfig == NULL) {
49559 return MA_INVALID_ARGS;
49560 }
49561
49562 bqConfig = ma_notch2__get_biquad_config(pConfig);
49563 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
49564 if (result != MA_SUCCESS) {
49565 return result;
49566 }
49567
49568 return MA_SUCCESS;
49569}
49570
49571MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter)
49572{
49573 ma_result result;
49574 size_t heapSizeInBytes;
49575 void* pHeap;
49576
49577 result = ma_notch2_get_heap_size(pConfig, &heapSizeInBytes);
49578 if (result != MA_SUCCESS) {
49579 return result;
49580 }
49581
49582 if (heapSizeInBytes > 0) {
49583 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
49584 if (pHeap == NULL) {
49585 return MA_OUT_OF_MEMORY;
49586 }
49587 } else {
49588 pHeap = NULL;
49589 }
49590
49591 result = ma_notch2_init_preallocated(pConfig, pHeap, pFilter);
49592 if (result != MA_SUCCESS) {
49593 ma_free(pHeap, pAllocationCallbacks);
49594 return result;
49595 }
49596
49597 pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
49598 return MA_SUCCESS;
49599}
49600
49601MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
49602{
49603 if (pFilter == NULL) {
49604 return;
49605 }
49606
49607 ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
49608}
49609
49611{
49612 ma_result result;
49613 ma_biquad_config bqConfig;
49614
49615 if (pFilter == NULL || pConfig == NULL) {
49616 return MA_INVALID_ARGS;
49617 }
49618
49619 bqConfig = ma_notch2__get_biquad_config(pConfig);
49620 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
49621 if (result != MA_SUCCESS) {
49622 return result;
49623 }
49624
49625 return MA_SUCCESS;
49626}
49627
49628static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
49629{
49630 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
49631}
49632
49633static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn)
49634{
49635 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
49636}
49637
49638MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
49639{
49640 if (pFilter == NULL) {
49641 return MA_INVALID_ARGS;
49642 }
49643
49644 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
49645}
49646
49648{
49649 if (pFilter == NULL) {
49650 return 0;
49651 }
49652
49653 return ma_biquad_get_latency(&pFilter->bq);
49654}
49655
49656
49657
49658/**************************************************************************************************************************************************************
49659
49660Peaking EQ Filter
49661
49662**************************************************************************************************************************************************************/
49663MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
49664{
49665 ma_peak2_config config;
49666
49667 MA_ZERO_OBJECT(&config);
49668 config.format = format;
49669 config.channels = channels;
49670 config.sampleRate = sampleRate;
49671 config.gainDB = gainDB;
49672 config.q = q;
49673 config.frequency = frequency;
49674
49675 if (config.q == 0) {
49676 config.q = 0.707107;
49677 }
49678
49679 return config;
49680}
49681
49682
49683static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig)
49684{
49685 ma_biquad_config bqConfig;
49686 double q;
49687 double w;
49688 double s;
49689 double c;
49690 double a;
49691 double A;
49692
49693 MA_ASSERT(pConfig != NULL);
49694
49695 q = pConfig->q;
49696 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
49697 s = ma_sind(w);
49698 c = ma_cosd(w);
49699 a = s / (2*q);
49700 A = ma_powd(10, (pConfig->gainDB / 40));
49701
49702 bqConfig.b0 = 1 + (a * A);
49703 bqConfig.b1 = -2 * c;
49704 bqConfig.b2 = 1 - (a * A);
49705 bqConfig.a0 = 1 + (a / A);
49706 bqConfig.a1 = -2 * c;
49707 bqConfig.a2 = 1 - (a / A);
49708
49709 bqConfig.format = pConfig->format;
49710 bqConfig.channels = pConfig->channels;
49711
49712 return bqConfig;
49713}
49714
49715MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes)
49716{
49717 ma_biquad_config bqConfig;
49718 bqConfig = ma_peak2__get_biquad_config(pConfig);
49719
49720 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
49721}
49722
49723MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter)
49724{
49725 ma_result result;
49726 ma_biquad_config bqConfig;
49727
49728 if (pFilter == NULL) {
49729 return MA_INVALID_ARGS;
49730 }
49731
49732 MA_ZERO_OBJECT(pFilter);
49733
49734 if (pConfig == NULL) {
49735 return MA_INVALID_ARGS;
49736 }
49737
49738 bqConfig = ma_peak2__get_biquad_config(pConfig);
49739 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
49740 if (result != MA_SUCCESS) {
49741 return result;
49742 }
49743
49744 return MA_SUCCESS;
49745}
49746
49747MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter)
49748{
49749 ma_result result;
49750 size_t heapSizeInBytes;
49751 void* pHeap;
49752
49753 result = ma_peak2_get_heap_size(pConfig, &heapSizeInBytes);
49754 if (result != MA_SUCCESS) {
49755 return result;
49756 }
49757
49758 if (heapSizeInBytes > 0) {
49759 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
49760 if (pHeap == NULL) {
49761 return MA_OUT_OF_MEMORY;
49762 }
49763 } else {
49764 pHeap = NULL;
49765 }
49766
49767 result = ma_peak2_init_preallocated(pConfig, pHeap, pFilter);
49768 if (result != MA_SUCCESS) {
49769 ma_free(pHeap, pAllocationCallbacks);
49770 return result;
49771 }
49772
49773 pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
49774 return MA_SUCCESS;
49775}
49776
49777MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
49778{
49779 if (pFilter == NULL) {
49780 return;
49781 }
49782
49783 ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
49784}
49785
49787{
49788 ma_result result;
49789 ma_biquad_config bqConfig;
49790
49791 if (pFilter == NULL || pConfig == NULL) {
49792 return MA_INVALID_ARGS;
49793 }
49794
49795 bqConfig = ma_peak2__get_biquad_config(pConfig);
49796 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
49797 if (result != MA_SUCCESS) {
49798 return result;
49799 }
49800
49801 return MA_SUCCESS;
49802}
49803
49804static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
49805{
49806 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
49807}
49808
49809static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn)
49810{
49811 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
49812}
49813
49814MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
49815{
49816 if (pFilter == NULL) {
49817 return MA_INVALID_ARGS;
49818 }
49819
49820 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
49821}
49822
49824{
49825 if (pFilter == NULL) {
49826 return 0;
49827 }
49828
49829 return ma_biquad_get_latency(&pFilter->bq);
49830}
49831
49832
49833/**************************************************************************************************************************************************************
49834
49835Low Shelf Filter
49836
49837**************************************************************************************************************************************************************/
49838MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
49839{
49840 ma_loshelf2_config config;
49841
49842 MA_ZERO_OBJECT(&config);
49843 config.format = format;
49844 config.channels = channels;
49845 config.sampleRate = sampleRate;
49846 config.gainDB = gainDB;
49847 config.shelfSlope = shelfSlope;
49848 config.frequency = frequency;
49849
49850 return config;
49851}
49852
49853
49854static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig)
49855{
49856 ma_biquad_config bqConfig;
49857 double w;
49858 double s;
49859 double c;
49860 double A;
49861 double S;
49862 double a;
49863 double sqrtA;
49864
49865 MA_ASSERT(pConfig != NULL);
49866
49867 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
49868 s = ma_sind(w);
49869 c = ma_cosd(w);
49870 A = ma_powd(10, (pConfig->gainDB / 40));
49871 S = pConfig->shelfSlope;
49872 a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2);
49873 sqrtA = 2*ma_sqrtd(A)*a;
49874
49875 bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA);
49876 bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c);
49877 bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA);
49878 bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA;
49879 bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c);
49880 bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA;
49881
49882 bqConfig.format = pConfig->format;
49883 bqConfig.channels = pConfig->channels;
49884
49885 return bqConfig;
49886}
49887
49888MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes)
49889{
49890 ma_biquad_config bqConfig;
49891 bqConfig = ma_loshelf2__get_biquad_config(pConfig);
49892
49893 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
49894}
49895
49897{
49898 ma_result result;
49899 ma_biquad_config bqConfig;
49900
49901 if (pFilter == NULL) {
49902 return MA_INVALID_ARGS;
49903 }
49904
49905 MA_ZERO_OBJECT(pFilter);
49906
49907 if (pConfig == NULL) {
49908 return MA_INVALID_ARGS;
49909 }
49910
49911 bqConfig = ma_loshelf2__get_biquad_config(pConfig);
49912 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
49913 if (result != MA_SUCCESS) {
49914 return result;
49915 }
49916
49917 return MA_SUCCESS;
49918}
49919
49920MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter)
49921{
49922 ma_result result;
49923 size_t heapSizeInBytes;
49924 void* pHeap;
49925
49926 result = ma_loshelf2_get_heap_size(pConfig, &heapSizeInBytes);
49927 if (result != MA_SUCCESS) {
49928 return result;
49929 }
49930
49931 if (heapSizeInBytes > 0) {
49932 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
49933 if (pHeap == NULL) {
49934 return MA_OUT_OF_MEMORY;
49935 }
49936 } else {
49937 pHeap = NULL;
49938 }
49939
49940 result = ma_loshelf2_init_preallocated(pConfig, pHeap, pFilter);
49941 if (result != MA_SUCCESS) {
49942 ma_free(pHeap, pAllocationCallbacks);
49943 return result;
49944 }
49945
49946 pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
49947 return MA_SUCCESS;
49948}
49949
49950MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
49951{
49952 if (pFilter == NULL) {
49953 return;
49954 }
49955
49956 ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
49957}
49958
49960{
49961 ma_result result;
49962 ma_biquad_config bqConfig;
49963
49964 if (pFilter == NULL || pConfig == NULL) {
49965 return MA_INVALID_ARGS;
49966 }
49967
49968 bqConfig = ma_loshelf2__get_biquad_config(pConfig);
49969 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
49970 if (result != MA_SUCCESS) {
49971 return result;
49972 }
49973
49974 return MA_SUCCESS;
49975}
49976
49977static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
49978{
49979 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
49980}
49981
49982static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn)
49983{
49984 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
49985}
49986
49987MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
49988{
49989 if (pFilter == NULL) {
49990 return MA_INVALID_ARGS;
49991 }
49992
49993 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
49994}
49995
49997{
49998 if (pFilter == NULL) {
49999 return 0;
50000 }
50001
50002 return ma_biquad_get_latency(&pFilter->bq);
50003}
50004
50005
50006/**************************************************************************************************************************************************************
50007
50008High Shelf Filter
50009
50010**************************************************************************************************************************************************************/
50011MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
50012{
50013 ma_hishelf2_config config;
50014
50015 MA_ZERO_OBJECT(&config);
50016 config.format = format;
50017 config.channels = channels;
50018 config.sampleRate = sampleRate;
50019 config.gainDB = gainDB;
50020 config.shelfSlope = shelfSlope;
50021 config.frequency = frequency;
50022
50023 return config;
50024}
50025
50026
50027static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig)
50028{
50029 ma_biquad_config bqConfig;
50030 double w;
50031 double s;
50032 double c;
50033 double A;
50034 double S;
50035 double a;
50036 double sqrtA;
50037
50038 MA_ASSERT(pConfig != NULL);
50039
50040 w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
50041 s = ma_sind(w);
50042 c = ma_cosd(w);
50043 A = ma_powd(10, (pConfig->gainDB / 40));
50044 S = pConfig->shelfSlope;
50045 a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2);
50046 sqrtA = 2*ma_sqrtd(A)*a;
50047
50048 bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA);
50049 bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c);
50050 bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA);
50051 bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA;
50052 bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c);
50053 bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA;
50054
50055 bqConfig.format = pConfig->format;
50056 bqConfig.channels = pConfig->channels;
50057
50058 return bqConfig;
50059}
50060
50061MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes)
50062{
50063 ma_biquad_config bqConfig;
50064 bqConfig = ma_hishelf2__get_biquad_config(pConfig);
50065
50066 return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
50067}
50068
50070{
50071 ma_result result;
50072 ma_biquad_config bqConfig;
50073
50074 if (pFilter == NULL) {
50075 return MA_INVALID_ARGS;
50076 }
50077
50078 MA_ZERO_OBJECT(pFilter);
50079
50080 if (pConfig == NULL) {
50081 return MA_INVALID_ARGS;
50082 }
50083
50084 bqConfig = ma_hishelf2__get_biquad_config(pConfig);
50085 result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
50086 if (result != MA_SUCCESS) {
50087 return result;
50088 }
50089
50090 return MA_SUCCESS;
50091}
50092
50093MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter)
50094{
50095 ma_result result;
50096 size_t heapSizeInBytes;
50097 void* pHeap;
50098
50099 result = ma_hishelf2_get_heap_size(pConfig, &heapSizeInBytes);
50100 if (result != MA_SUCCESS) {
50101 return result;
50102 }
50103
50104 if (heapSizeInBytes > 0) {
50105 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
50106 if (pHeap == NULL) {
50107 return MA_OUT_OF_MEMORY;
50108 }
50109 } else {
50110 pHeap = NULL;
50111 }
50112
50113 result = ma_hishelf2_init_preallocated(pConfig, pHeap, pFilter);
50114 if (result != MA_SUCCESS) {
50115 ma_free(pHeap, pAllocationCallbacks);
50116 return result;
50117 }
50118
50119 pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */
50120 return MA_SUCCESS;
50121}
50122
50123MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
50124{
50125 if (pFilter == NULL) {
50126 return;
50127 }
50128
50129 ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */
50130}
50131
50133{
50134 ma_result result;
50135 ma_biquad_config bqConfig;
50136
50137 if (pFilter == NULL || pConfig == NULL) {
50138 return MA_INVALID_ARGS;
50139 }
50140
50141 bqConfig = ma_hishelf2__get_biquad_config(pConfig);
50142 result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
50143 if (result != MA_SUCCESS) {
50144 return result;
50145 }
50146
50147 return MA_SUCCESS;
50148}
50149
50150static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
50151{
50152 ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
50153}
50154
50155static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn)
50156{
50157 ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
50158}
50159
50160MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
50161{
50162 if (pFilter == NULL) {
50163 return MA_INVALID_ARGS;
50164 }
50165
50166 return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
50167}
50168
50170{
50171 if (pFilter == NULL) {
50172 return 0;
50173 }
50174
50175 return ma_biquad_get_latency(&pFilter->bq);
50176}
50177
50178
50179
50180/*
50181Delay
50182*/
50183MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)
50184{
50185 ma_delay_config config;
50186
50187 MA_ZERO_OBJECT(&config);
50188 config.channels = channels;
50189 config.sampleRate = sampleRate;
50190 config.delayInFrames = delayInFrames;
50191 config.delayStart = (decay == 0) ? MA_TRUE : MA_FALSE; /* Delay the start if it looks like we're not configuring an echo. */
50192 config.wet = 1;
50193 config.dry = 1;
50194 config.decay = decay;
50195
50196 return config;
50197}
50198
50199
50200MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay)
50201{
50202 if (pDelay == NULL) {
50203 return MA_INVALID_ARGS;
50204 }
50205
50206 MA_ZERO_OBJECT(pDelay);
50207
50208 if (pConfig == NULL) {
50209 return MA_INVALID_ARGS;
50210 }
50211
50212 if (pConfig->decay < 0 || pConfig->decay > 1) {
50213 return MA_INVALID_ARGS;
50214 }
50215
50216 pDelay->config = *pConfig;
50217 pDelay->bufferSizeInFrames = pConfig->delayInFrames;
50218 pDelay->cursor = 0;
50219
50220 pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks);
50221 if (pDelay->pBuffer == NULL) {
50222 return MA_OUT_OF_MEMORY;
50223 }
50224
50226
50227 return MA_SUCCESS;
50228}
50229
50230MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks)
50231{
50232 if (pDelay == NULL) {
50233 return;
50234 }
50235
50236 ma_free(pDelay->pBuffer, pAllocationCallbacks);
50237}
50238
50239MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
50240{
50241 ma_uint32 iFrame;
50242 ma_uint32 iChannel;
50243 float* pFramesOutF32 = (float*)pFramesOut;
50244 const float* pFramesInF32 = (const float*)pFramesIn;
50245
50246 if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) {
50247 return MA_INVALID_ARGS;
50248 }
50249
50250 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50251 for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) {
50252 ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel;
50253
50254 if (pDelay->config.delayStart) {
50255 /* Delayed start. */
50256
50257 /* Read */
50258 pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet;
50259
50260 /* Feedback */
50261 pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry);
50262 } else {
50263 /* Immediate start */
50264
50265 /* Feedback */
50266 pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry);
50267
50268 /* Read */
50269 pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet;
50270 }
50271 }
50272
50273 pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames;
50274
50275 pFramesOutF32 += pDelay->config.channels;
50276 pFramesInF32 += pDelay->config.channels;
50277 }
50278
50279 return MA_SUCCESS;
50280}
50281
50282MA_API void ma_delay_set_wet(ma_delay* pDelay, float value)
50283{
50284 if (pDelay == NULL) {
50285 return;
50286 }
50287
50288 pDelay->config.wet = value;
50289}
50290
50291MA_API float ma_delay_get_wet(const ma_delay* pDelay)
50292{
50293 if (pDelay == NULL) {
50294 return 0;
50295 }
50296
50297 return pDelay->config.wet;
50298}
50299
50300MA_API void ma_delay_set_dry(ma_delay* pDelay, float value)
50301{
50302 if (pDelay == NULL) {
50303 return;
50304 }
50305
50306 pDelay->config.dry = value;
50307}
50308
50309MA_API float ma_delay_get_dry(const ma_delay* pDelay)
50310{
50311 if (pDelay == NULL) {
50312 return 0;
50313 }
50314
50315 return pDelay->config.dry;
50316}
50317
50318MA_API void ma_delay_set_decay(ma_delay* pDelay, float value)
50319{
50320 if (pDelay == NULL) {
50321 return;
50322 }
50323
50324 pDelay->config.decay = value;
50325}
50326
50327MA_API float ma_delay_get_decay(const ma_delay* pDelay)
50328{
50329 if (pDelay == NULL) {
50330 return 0;
50331 }
50332
50333 return pDelay->config.decay;
50334}
50335
50336
50338{
50339 ma_gainer_config config;
50340
50341 MA_ZERO_OBJECT(&config);
50342 config.channels = channels;
50343 config.smoothTimeInFrames = smoothTimeInFrames;
50344
50345 return config;
50346}
50347
50348
50349typedef struct
50350{
50351 size_t sizeInBytes;
50352 size_t oldGainsOffset;
50353 size_t newGainsOffset;
50354} ma_gainer_heap_layout;
50355
50356static ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout)
50357{
50358 MA_ASSERT(pHeapLayout != NULL);
50359
50360 MA_ZERO_OBJECT(pHeapLayout);
50361
50362 if (pConfig == NULL) {
50363 return MA_INVALID_ARGS;
50364 }
50365
50366 if (pConfig->channels == 0) {
50367 return MA_INVALID_ARGS;
50368 }
50369
50370 pHeapLayout->sizeInBytes = 0;
50371
50372 /* Old gains. */
50373 pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes;
50374 pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
50375
50376 /* New gains. */
50377 pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes;
50378 pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
50379
50380 /* Alignment. */
50381 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
50382
50383 return MA_SUCCESS;
50384}
50385
50386
50387MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes)
50388{
50389 ma_result result;
50390 ma_gainer_heap_layout heapLayout;
50391
50392 if (pHeapSizeInBytes == NULL) {
50393 return MA_INVALID_ARGS;
50394 }
50395
50396 *pHeapSizeInBytes = 0;
50397
50398 result = ma_gainer_get_heap_layout(pConfig, &heapLayout);
50399 if (result != MA_SUCCESS) {
50400 return MA_INVALID_ARGS;
50401 }
50402
50403 *pHeapSizeInBytes = heapLayout.sizeInBytes;
50404
50405 return MA_SUCCESS;
50406}
50407
50408
50409MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer)
50410{
50411 ma_result result;
50412 ma_gainer_heap_layout heapLayout;
50413 ma_uint32 iChannel;
50414
50415 if (pGainer == NULL) {
50416 return MA_INVALID_ARGS;
50417 }
50418
50419 MA_ZERO_OBJECT(pGainer);
50420
50421 if (pConfig == NULL || pHeap == NULL) {
50422 return MA_INVALID_ARGS;
50423 }
50424
50425 result = ma_gainer_get_heap_layout(pConfig, &heapLayout);
50426 if (result != MA_SUCCESS) {
50427 return result;
50428 }
50429
50430 pGainer->_pHeap = pHeap;
50431 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
50432
50433 pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset);
50434 pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset);
50435 pGainer->masterVolume = 1;
50436
50437 pGainer->config = *pConfig;
50438 pGainer->t = (ma_uint32)-1; /* No interpolation by default. */
50439
50440 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
50441 pGainer->pOldGains[iChannel] = 1;
50442 pGainer->pNewGains[iChannel] = 1;
50443 }
50444
50445 return MA_SUCCESS;
50446}
50447
50448MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer)
50449{
50450 ma_result result;
50451 size_t heapSizeInBytes;
50452 void* pHeap;
50453
50454 result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes);
50455 if (result != MA_SUCCESS) {
50456 return result; /* Failed to retrieve the size of the heap allocation. */
50457 }
50458
50459 if (heapSizeInBytes > 0) {
50460 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
50461 if (pHeap == NULL) {
50462 return MA_OUT_OF_MEMORY;
50463 }
50464 } else {
50465 pHeap = NULL;
50466 }
50467
50468 result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer);
50469 if (result != MA_SUCCESS) {
50470 ma_free(pHeap, pAllocationCallbacks);
50471 return result;
50472 }
50473
50474 pGainer->_ownsHeap = MA_TRUE;
50475 return MA_SUCCESS;
50476}
50477
50478MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks)
50479{
50480 if (pGainer == NULL) {
50481 return;
50482 }
50483
50484 if (pGainer->_ownsHeap) {
50485 ma_free(pGainer->_pHeap, pAllocationCallbacks);
50486 }
50487}
50488
50489static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel)
50490{
50491 float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
50492 return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a);
50493}
50494
50495static /*__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)
50496{
50497 ma_uint64 iFrame;
50498 ma_uint32 iChannel;
50499 ma_uint64 interpolatedFrameCount;
50500
50501 MA_ASSERT(pGainer != NULL);
50502
50503 /*
50504 We don't necessarily need to apply a linear interpolation for the entire frameCount frames. When
50505 linear interpolation is not needed we can do a simple volume adjustment which will be more
50506 efficient than a lerp with an alpha value of 1.
50507
50508 To do this, all we need to do is determine how many frames need to have a lerp applied. Then we
50509 just process that number of frames with linear interpolation. After that we run on an optimized
50510 path which just applies the new gains without a lerp.
50511 */
50512 if (pGainer->t >= pGainer->config.smoothTimeInFrames) {
50513 interpolatedFrameCount = 0;
50514 } else {
50515 interpolatedFrameCount = pGainer->t - pGainer->config.smoothTimeInFrames;
50516 if (interpolatedFrameCount > frameCount) {
50517 interpolatedFrameCount = frameCount;
50518 }
50519 }
50520
50521 /*
50522 Start off with our interpolated frames. When we do this, we'll adjust frameCount and our pointers
50523 so that the fast path can work naturally without consideration of the interpolated path.
50524 */
50525 if (interpolatedFrameCount > 0) {
50526 /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
50527 if (pFramesOut != NULL && pFramesIn != NULL) {
50528 /*
50529 All we're really doing here is moving the old gains towards the new gains. We don't want to
50530 be modifying the gains inside the ma_gainer object because that will break things. Instead
50531 we can make a copy here on the stack. For extreme channel counts we can fall back to a slower
50532 implementation which just uses a standard lerp.
50533 */
50534 float* pFramesOutF32 = (float*)pFramesOut;
50535 const float* pFramesInF32 = (const float*)pFramesIn;
50536 float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
50537 float d = 1.0f / pGainer->config.smoothTimeInFrames;
50538
50539 if (pGainer->config.channels <= 32) {
50540 float pRunningGain[32];
50541 float pRunningGainDelta[32]; /* Could this be heap-allocated as part of the ma_gainer object? */
50542
50543 /* Initialize the running gain. */
50544 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
50545 float t = (pGainer->pNewGains[iChannel] - pGainer->pOldGains[iChannel]) * pGainer->masterVolume;
50546 pRunningGainDelta[iChannel] = t * d;
50547 pRunningGain[iChannel] = (pGainer->pOldGains[iChannel] * pGainer->masterVolume) + (t * a);
50548 }
50549
50550 iFrame = 0;
50551
50552 /* Optimized paths for common channel counts. This is mostly just experimenting with some SIMD ideas. It's not necessarily final. */
50553 if (pGainer->config.channels == 2) {
50554 #if defined(MA_SUPPORT_SSE2)
50555 if (ma_has_sse2()) {
50556 ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1;
50557
50558 /* Expand some arrays so we can have a clean SIMD loop below. */
50559 __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[1], pRunningGainDelta[0]);
50560 __m128 runningGain0 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[1], pRunningGain[0]);
50561
50562 for (; iFrame < unrolledLoopCount; iFrame += 1) {
50563 _mm_storeu_ps(&pFramesOutF32[iFrame*4 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*4 + 0]), runningGain0));
50564 runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0);
50565 }
50566
50567 iFrame = unrolledLoopCount << 1;
50568 } else
50569 #endif
50570 {
50571 /*
50572 Two different scalar implementations here. Clang (and I assume GCC) will vectorize
50573 both of these, but the bottom version results in a nicer vectorization with less
50574 instructions emitted. The problem, however, is that the bottom version runs slower
50575 when compiled with MSVC. The top version will be partially vectorized by MSVC.
50576 */
50577 #if defined(_MSC_VER) && !defined(__clang__)
50578 ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1;
50579
50580 /* Expand some arrays so we can have a clean 4x SIMD operation in the loop. */
50581 pRunningGainDelta[2] = pRunningGainDelta[0];
50582 pRunningGainDelta[3] = pRunningGainDelta[1];
50583 pRunningGain[2] = pRunningGain[0] + pRunningGainDelta[0];
50584 pRunningGain[3] = pRunningGain[1] + pRunningGainDelta[1];
50585
50586 for (; iFrame < unrolledLoopCount; iFrame += 1) {
50587 pFramesOutF32[iFrame*4 + 0] = pFramesInF32[iFrame*4 + 0] * pRunningGain[0];
50588 pFramesOutF32[iFrame*4 + 1] = pFramesInF32[iFrame*4 + 1] * pRunningGain[1];
50589 pFramesOutF32[iFrame*4 + 2] = pFramesInF32[iFrame*4 + 2] * pRunningGain[2];
50590 pFramesOutF32[iFrame*4 + 3] = pFramesInF32[iFrame*4 + 3] * pRunningGain[3];
50591
50592 /* Move the running gain forward towards the new gain. */
50593 pRunningGain[0] += pRunningGainDelta[0];
50594 pRunningGain[1] += pRunningGainDelta[1];
50595 pRunningGain[2] += pRunningGainDelta[2];
50596 pRunningGain[3] += pRunningGainDelta[3];
50597 }
50598
50599 iFrame = unrolledLoopCount << 1;
50600 #else
50601 for (; iFrame < interpolatedFrameCount; iFrame += 1) {
50602 for (iChannel = 0; iChannel < 2; iChannel += 1) {
50603 pFramesOutF32[iFrame*2 + iChannel] = pFramesInF32[iFrame*2 + iChannel] * pRunningGain[iChannel];
50604 }
50605
50606 for (iChannel = 0; iChannel < 2; iChannel += 1) {
50607 pRunningGain[iChannel] += pRunningGainDelta[iChannel];
50608 }
50609 }
50610 #endif
50611 }
50612 } else if (pGainer->config.channels == 6) {
50613 #if defined(MA_SUPPORT_SSE2)
50614 if (ma_has_sse2()) {
50615 /*
50616 For 6 channels things are a bit more complicated because 6 isn't cleanly divisible by 4. We need to do 2 frames
50617 at a time, meaning we'll be doing 12 samples in a group. Like the stereo case we'll need to expand some arrays
50618 so we can do clean 4x SIMD operations.
50619 */
50620 ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1;
50621
50622 /* Expand some arrays so we can have a clean SIMD loop below. */
50623 __m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[3], pRunningGainDelta[2], pRunningGainDelta[1], pRunningGainDelta[0]);
50624 __m128 runningGainDelta1 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[5], pRunningGainDelta[4]);
50625 __m128 runningGainDelta2 = _mm_set_ps(pRunningGainDelta[5], pRunningGainDelta[4], pRunningGainDelta[3], pRunningGainDelta[2]);
50626
50627 __m128 runningGain0 = _mm_set_ps(pRunningGain[3], pRunningGain[2], pRunningGain[1], pRunningGain[0]);
50628 __m128 runningGain1 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[5], pRunningGain[4]);
50629 __m128 runningGain2 = _mm_set_ps(pRunningGain[5] + pRunningGainDelta[5], pRunningGain[4] + pRunningGainDelta[4], pRunningGain[3] + pRunningGainDelta[3], pRunningGain[2] + pRunningGainDelta[2]);
50630
50631 for (; iFrame < unrolledLoopCount; iFrame += 1) {
50632 _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 0]), runningGain0));
50633 _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 4]), runningGain1));
50634 _mm_storeu_ps(&pFramesOutF32[iFrame*12 + 8], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 8]), runningGain2));
50635
50636 runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0);
50637 runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1);
50638 runningGain2 = _mm_add_ps(runningGain2, runningGainDelta2);
50639 }
50640
50641 iFrame = unrolledLoopCount << 1;
50642 } else
50643 #endif
50644 {
50645 for (; iFrame < interpolatedFrameCount; iFrame += 1) {
50646 for (iChannel = 0; iChannel < 6; iChannel += 1) {
50647 pFramesOutF32[iFrame*6 + iChannel] = pFramesInF32[iFrame*6 + iChannel] * pRunningGain[iChannel];
50648 }
50649
50650 /* Move the running gain forward towards the new gain. */
50651 for (iChannel = 0; iChannel < 6; iChannel += 1) {
50652 pRunningGain[iChannel] += pRunningGainDelta[iChannel];
50653 }
50654 }
50655 }
50656 } else if (pGainer->config.channels == 8) {
50657 /* For 8 channels we can just go over frame by frame and do all eight channels as 2 separate 4x SIMD operations. */
50658 #if defined(MA_SUPPORT_SSE2)
50659 if (ma_has_sse2()) {
50660 __m128 runningGainDelta0 = _mm_loadu_ps(&pRunningGainDelta[0]);
50661 __m128 runningGainDelta1 = _mm_loadu_ps(&pRunningGainDelta[4]);
50662 __m128 runningGain0 = _mm_loadu_ps(&pRunningGain[0]);
50663 __m128 runningGain1 = _mm_loadu_ps(&pRunningGain[4]);
50664
50665 for (; iFrame < interpolatedFrameCount; iFrame += 1) {
50666 _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 0]), runningGain0));
50667 _mm_storeu_ps(&pFramesOutF32[iFrame*8 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 4]), runningGain1));
50668
50669 runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0);
50670 runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1);
50671 }
50672 } else
50673 #endif
50674 {
50675 /* This is crafted so that it auto-vectorizes when compiled with Clang. */
50676 for (; iFrame < interpolatedFrameCount; iFrame += 1) {
50677 for (iChannel = 0; iChannel < 8; iChannel += 1) {
50678 pFramesOutF32[iFrame*8 + iChannel] = pFramesInF32[iFrame*8 + iChannel] * pRunningGain[iChannel];
50679 }
50680
50681 /* Move the running gain forward towards the new gain. */
50682 for (iChannel = 0; iChannel < 8; iChannel += 1) {
50683 pRunningGain[iChannel] += pRunningGainDelta[iChannel];
50684 }
50685 }
50686 }
50687 }
50688
50689 for (; iFrame < interpolatedFrameCount; iFrame += 1) {
50690 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
50691 pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * pRunningGain[iChannel];
50692 pRunningGain[iChannel] += pRunningGainDelta[iChannel];
50693 }
50694 }
50695 } else {
50696 /* 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. */
50697 for (iFrame = 0; iFrame < interpolatedFrameCount; iFrame += 1) {
50698 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
50699 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;
50700 }
50701
50702 a += d;
50703 }
50704 }
50705 }
50706
50707 /* Make sure the timer is updated. */
50708 pGainer->t = (ma_uint32)ma_min(pGainer->t + interpolatedFrameCount, pGainer->config.smoothTimeInFrames);
50709
50710 /* Adjust our arguments so the next part can work normally. */
50711 frameCount -= interpolatedFrameCount;
50712 pFramesOut = ma_offset_ptr(pFramesOut, interpolatedFrameCount * sizeof(float));
50713 pFramesIn = ma_offset_ptr(pFramesIn, interpolatedFrameCount * sizeof(float));
50714 }
50715
50716 /* All we need to do here is apply the new gains using an optimized path. */
50717 if (pFramesOut != NULL && pFramesIn != NULL) {
50718 if (pGainer->config.channels <= 32) {
50719 float gains[32];
50720 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
50721 gains[iChannel] = pGainer->pNewGains[iChannel] * pGainer->masterVolume;
50722 }
50723
50724 ma_copy_and_apply_volume_factor_per_channel_f32((float*)pFramesOut, (const float*)pFramesIn, frameCount, pGainer->config.channels, gains);
50725 } else {
50726 /* Slow path. Too many channels to fit on the stack. Need to apply a master volume as a separate path. */
50727 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50728 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
50729 ((float*)pFramesOut)[iFrame*pGainer->config.channels + iChannel] = ((const float*)pFramesIn)[iFrame*pGainer->config.channels + iChannel] * pGainer->pNewGains[iChannel] * pGainer->masterVolume;
50730 }
50731 }
50732 }
50733 }
50734
50735 /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */
50736 if (pGainer->t == (ma_uint32)-1) {
50737 pGainer->t = (ma_uint32)ma_min(pGainer->config.smoothTimeInFrames, frameCount);
50738 }
50739
50740#if 0
50741 if (pGainer->t >= pGainer->config.smoothTimeInFrames) {
50742 /* Fast path. No gain calculation required. */
50743 ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains);
50744 ma_apply_volume_factor_f32(pFramesOutF32, frameCount * pGainer->config.channels, pGainer->masterVolume);
50745
50746 /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */
50747 if (pGainer->t == (ma_uint32)-1) {
50748 pGainer->t = pGainer->config.smoothTimeInFrames;
50749 }
50750 } else {
50751 /* Slow path. Need to interpolate the gain for each channel individually. */
50752
50753 /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
50754 if (pFramesOut != NULL && pFramesIn != NULL) {
50755 float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
50756 float d = 1.0f / pGainer->config.smoothTimeInFrames;
50757 ma_uint32 channelCount = pGainer->config.channels;
50758
50759 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50760 for (iChannel = 0; iChannel < channelCount; iChannel += 1) {
50761 pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume;
50762 }
50763
50764 pFramesOutF32 += channelCount;
50765 pFramesInF32 += channelCount;
50766
50767 a += d;
50768 if (a > 1) {
50769 a = 1;
50770 }
50771 }
50772 }
50773
50774 pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames);
50775
50776 #if 0 /* Reference implementation. */
50777 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50778 /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
50779 if (pFramesOut != NULL && pFramesIn != NULL) {
50780 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
50781 pFramesOutF32[iFrame * pGainer->config.channels + iChannel] = pFramesInF32[iFrame * pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel) * pGainer->masterVolume;
50782 }
50783 }
50784
50785 /* Move interpolation time forward, but don't go beyond our smoothing time. */
50786 pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames);
50787 }
50788 #endif
50789 }
50790#endif
50791
50792 return MA_SUCCESS;
50793}
50794
50795MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
50796{
50797 if (pGainer == NULL) {
50798 return MA_INVALID_ARGS;
50799 }
50800
50801 /*
50802 ma_gainer_process_pcm_frames_internal() marks pFramesOut and pFramesIn with MA_RESTRICT which
50803 helps with auto-vectorization.
50804 */
50805 return ma_gainer_process_pcm_frames_internal(pGainer, pFramesOut, pFramesIn, frameCount);
50806}
50807
50808static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel)
50809{
50810 pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel);
50811 pGainer->pNewGains[iChannel] = newGain;
50812}
50813
50814static void ma_gainer_reset_smoothing_time(ma_gainer* pGainer)
50815{
50816 if (pGainer->t == (ma_uint32)-1) {
50817 pGainer->t = pGainer->config.smoothTimeInFrames; /* No smoothing required for initial gains setting. */
50818 } else {
50819 pGainer->t = 0;
50820 }
50821}
50822
50823MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain)
50824{
50825 ma_uint32 iChannel;
50826
50827 if (pGainer == NULL) {
50828 return MA_INVALID_ARGS;
50829 }
50830
50831 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
50832 ma_gainer_set_gain_by_index(pGainer, newGain, iChannel);
50833 }
50834
50835 /* 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. */
50836 ma_gainer_reset_smoothing_time(pGainer);
50837
50838 return MA_SUCCESS;
50839}
50840
50841MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains)
50842{
50843 ma_uint32 iChannel;
50844
50845 if (pGainer == NULL || pNewGains == NULL) {
50846 return MA_INVALID_ARGS;
50847 }
50848
50849 for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
50850 ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel);
50851 }
50852
50853 /* 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. */
50854 ma_gainer_reset_smoothing_time(pGainer);
50855
50856 return MA_SUCCESS;
50857}
50858
50860{
50861 if (pGainer == NULL) {
50862 return MA_INVALID_ARGS;
50863 }
50864
50865 pGainer->masterVolume = volume;
50866
50867 return MA_SUCCESS;
50868}
50869
50870MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume)
50871{
50872 if (pGainer == NULL || pVolume == NULL) {
50873 return MA_INVALID_ARGS;
50874 }
50875
50876 *pVolume = pGainer->masterVolume;
50877
50878 return MA_SUCCESS;
50879}
50880
50881
50883{
50884 ma_panner_config config;
50885
50886 MA_ZERO_OBJECT(&config);
50887 config.format = format;
50888 config.channels = channels;
50889 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. */
50890 config.pan = 0;
50891
50892 return config;
50893}
50894
50895
50897{
50898 if (pPanner == NULL) {
50899 return MA_INVALID_ARGS;
50900 }
50901
50902 MA_ZERO_OBJECT(pPanner);
50903
50904 if (pConfig == NULL) {
50905 return MA_INVALID_ARGS;
50906 }
50907
50908 pPanner->format = pConfig->format;
50909 pPanner->channels = pConfig->channels;
50910 pPanner->mode = pConfig->mode;
50911 pPanner->pan = pConfig->pan;
50912
50913 return MA_SUCCESS;
50914}
50915
50916static void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan)
50917{
50918 ma_uint64 iFrame;
50919
50920 if (pan > 0) {
50921 float factor = 1.0f - pan;
50922 if (pFramesOut == pFramesIn) {
50923 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50924 pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor;
50925 }
50926 } else {
50927 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50928 pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor;
50929 pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1];
50930 }
50931 }
50932 } else {
50933 float factor = 1.0f + pan;
50934 if (pFramesOut == pFramesIn) {
50935 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50936 pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor;
50937 }
50938 } else {
50939 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50940 pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0];
50941 pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor;
50942 }
50943 }
50944 }
50945}
50946
50947static void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan)
50948{
50949 if (pan == 0) {
50950 /* Fast path. No panning required. */
50951 if (pFramesOut == pFramesIn) {
50952 /* No-op */
50953 } else {
50954 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
50955 }
50956
50957 return;
50958 }
50959
50960 switch (format) {
50961 case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break;
50962
50963 /* Unknown format. Just copy. */
50964 default:
50965 {
50966 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
50967 } break;
50968 }
50969}
50970
50971
50972static void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan)
50973{
50974 ma_uint64 iFrame;
50975
50976 if (pan > 0) {
50977 float factorL0 = 1.0f - pan;
50978 float factorL1 = 0.0f + pan;
50979
50980 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50981 float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0);
50982 float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1];
50983
50984 pFramesOut[iFrame*2 + 0] = sample0;
50985 pFramesOut[iFrame*2 + 1] = sample1;
50986 }
50987 } else {
50988 float factorR0 = 0.0f - pan;
50989 float factorR1 = 1.0f + pan;
50990
50991 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
50992 float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0);
50993 float sample1 = (pFramesIn[iFrame*2 + 1] * factorR1);
50994
50995 pFramesOut[iFrame*2 + 0] = sample0;
50996 pFramesOut[iFrame*2 + 1] = sample1;
50997 }
50998 }
50999}
51000
51001static void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan)
51002{
51003 if (pan == 0) {
51004 /* Fast path. No panning required. */
51005 if (pFramesOut == pFramesIn) {
51006 /* No-op */
51007 } else {
51008 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
51009 }
51010
51011 return;
51012 }
51013
51014 switch (format) {
51015 case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break;
51016
51017 /* Unknown format. Just copy. */
51018 default:
51019 {
51020 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
51021 } break;
51022 }
51023}
51024
51025MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
51026{
51027 if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) {
51028 return MA_INVALID_ARGS;
51029 }
51030
51031 if (pPanner->channels == 2) {
51032 /* Stereo case. For now assume channel 0 is left and channel right is 1, but should probably add support for a channel map. */
51033 if (pPanner->mode == ma_pan_mode_balance) {
51034 ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan);
51035 } else {
51036 ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan);
51037 }
51038 } else {
51039 if (pPanner->channels == 1) {
51040 /* Panning has no effect on mono streams. */
51041 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels);
51042 } else {
51043 /* For now we're not going to support non-stereo set ups. Not sure how I want to handle this case just yet. */
51044 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels);
51045 }
51046 }
51047
51048 return MA_SUCCESS;
51049}
51050
51052{
51053 if (pPanner == NULL) {
51054 return;
51055 }
51056
51057 pPanner->mode = mode;
51058}
51059
51061{
51062 if (pPanner == NULL) {
51063 return ma_pan_mode_balance;
51064 }
51065
51066 return pPanner->mode;
51067}
51068
51069MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan)
51070{
51071 if (pPanner == NULL) {
51072 return;
51073 }
51074
51075 pPanner->pan = ma_clamp(pan, -1.0f, 1.0f);
51076}
51077
51078MA_API float ma_panner_get_pan(const ma_panner* pPanner)
51079{
51080 if (pPanner == NULL) {
51081 return 0;
51082 }
51083
51084 return pPanner->pan;
51085}
51086
51087
51088
51089
51091{
51092 ma_fader_config config;
51093
51094 MA_ZERO_OBJECT(&config);
51095 config.format = format;
51096 config.channels = channels;
51097 config.sampleRate = sampleRate;
51098
51099 return config;
51100}
51101
51102
51103MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader)
51104{
51105 if (pFader == NULL) {
51106 return MA_INVALID_ARGS;
51107 }
51108
51109 MA_ZERO_OBJECT(pFader);
51110
51111 if (pConfig == NULL) {
51112 return MA_INVALID_ARGS;
51113 }
51114
51115 /* Only f32 is supported for now. */
51116 if (pConfig->format != ma_format_f32) {
51117 return MA_INVALID_ARGS;
51118 }
51119
51120 pFader->config = *pConfig;
51121 pFader->volumeBeg = 1;
51122 pFader->volumeEnd = 1;
51123 pFader->lengthInFrames = 0;
51124 pFader->cursorInFrames = 0;
51125
51126 return MA_SUCCESS;
51127}
51128
51129MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
51130{
51131 if (pFader == NULL) {
51132 return MA_INVALID_ARGS;
51133 }
51134
51135 /* If the cursor is still negative we need to just copy the absolute number of those frames, but no more than frameCount. */
51136 if (pFader->cursorInFrames < 0) {
51137 ma_uint64 absCursorInFrames = (ma_uint64)0 - pFader->cursorInFrames;
51138 if (absCursorInFrames > frameCount) {
51139 absCursorInFrames = frameCount;
51140 }
51141
51142 ma_copy_pcm_frames(pFramesOut, pFramesIn, absCursorInFrames, pFader->config.format, pFader->config.channels);
51143
51144 pFader->cursorInFrames += absCursorInFrames;
51145 frameCount -= absCursorInFrames;
51146 pFramesOut = ma_offset_ptr(pFramesOut, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames);
51147 pFramesIn = ma_offset_ptr(pFramesIn, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames);
51148 }
51149
51150 if (pFader->cursorInFrames >= 0) {
51151 /*
51152 For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for
51153 the conversion to a float which we use for the linear interpolation. This might be changed later.
51154 */
51155 if (frameCount + pFader->cursorInFrames > UINT_MAX) {
51156 frameCount = UINT_MAX - pFader->cursorInFrames;
51157 }
51158
51159 /* Optimized path if volumeBeg and volumeEnd are equal. */
51160 if (pFader->volumeBeg == pFader->volumeEnd) {
51161 if (pFader->volumeBeg == 1) {
51162 /* Straight copy. */
51163 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels);
51164 } else {
51165 /* Copy with volume. */
51166 ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeBeg);
51167 }
51168 } else {
51169 /* Slower path. Volumes are different, so may need to do an interpolation. */
51170 if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) {
51171 /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */
51172 ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd);
51173 } else {
51174 /* Slow path. This is where we do the actual fading. */
51175 ma_uint64 iFrame;
51176 ma_uint32 iChannel;
51177
51178 /* For now we only support f32. Support for other formats might be added later. */
51179 if (pFader->config.format == ma_format_f32) {
51180 const float* pFramesInF32 = (const float*)pFramesIn;
51181 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
51182
51183 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
51184 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. */
51185 float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a);
51186
51187 for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) {
51188 pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume;
51189 }
51190 }
51191 } else {
51192 return MA_NOT_IMPLEMENTED;
51193 }
51194 }
51195 }
51196 }
51197
51198 pFader->cursorInFrames += frameCount;
51199
51200 return MA_SUCCESS;
51201}
51202
51203MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
51204{
51205 if (pFader == NULL) {
51206 return;
51207 }
51208
51209 if (pFormat != NULL) {
51210 *pFormat = pFader->config.format;
51211 }
51212
51213 if (pChannels != NULL) {
51214 *pChannels = pFader->config.channels;
51215 }
51216
51217 if (pSampleRate != NULL) {
51218 *pSampleRate = pFader->config.sampleRate;
51219 }
51220}
51221
51222MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames)
51223{
51224 ma_fader_set_fade_ex(pFader, volumeBeg, volumeEnd, lengthInFrames, 0);
51225}
51226
51227MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames)
51228{
51229 if (pFader == NULL) {
51230 return;
51231 }
51232
51233 /* If the volume is negative, use current volume. */
51234 if (volumeBeg < 0) {
51235 volumeBeg = ma_fader_get_current_volume(pFader);
51236 }
51237
51238 /*
51239 The length needs to be clamped to 32-bits due to how we convert it to a float for linear
51240 interpolation reasons. I might change this requirement later, but for now it's not important.
51241 */
51242 if (lengthInFrames > UINT_MAX) {
51243 lengthInFrames = UINT_MAX;
51244 }
51245
51246 /* The start offset needs to be clamped to ensure it doesn't overflow a signed number. */
51247 if (startOffsetInFrames > INT_MAX) {
51248 startOffsetInFrames = INT_MAX;
51249 }
51250
51251 pFader->volumeBeg = volumeBeg;
51252 pFader->volumeEnd = volumeEnd;
51253 pFader->lengthInFrames = lengthInFrames;
51254 pFader->cursorInFrames = -startOffsetInFrames;
51255}
51256
51257MA_API float ma_fader_get_current_volume(const ma_fader* pFader)
51258{
51259 if (pFader == NULL) {
51260 return 0.0f;
51261 }
51262
51263 /* Any frames prior to the start of the fade period will be at unfaded volume. */
51264 if (pFader->cursorInFrames < 0) {
51265 return 1.0f;
51266 }
51267
51268 /* The current volume depends on the position of the cursor. */
51269 if (pFader->cursorInFrames == 0) {
51270 return pFader->volumeBeg;
51271 } else if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { /* Safe case because the < 0 case was checked above. */
51272 return pFader->volumeEnd;
51273 } else {
51274 /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpolation between volumeBeg and volumeEnd based on our cursor position. */
51275 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(). */
51276 }
51277}
51278
51279
51280
51281
51282
51283MA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z)
51284{
51285 ma_vec3f v;
51286
51287 v.x = x;
51288 v.y = y;
51289 v.z = z;
51290
51291 return v;
51292}
51293
51294MA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b)
51295{
51296 return ma_vec3f_init_3f(
51297 a.x - b.x,
51298 a.y - b.y,
51299 a.z - b.z
51300 );
51301}
51302
51303MA_API ma_vec3f ma_vec3f_neg(ma_vec3f a)
51304{
51305 return ma_vec3f_init_3f(
51306 -a.x,
51307 -a.y,
51308 -a.z
51309 );
51310}
51311
51312MA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b)
51313{
51314 return a.x*b.x + a.y*b.y + a.z*b.z;
51315}
51316
51317MA_API float ma_vec3f_len2(ma_vec3f v)
51318{
51319 return ma_vec3f_dot(v, v);
51320}
51321
51322MA_API float ma_vec3f_len(ma_vec3f v)
51323{
51324 return (float)ma_sqrtd(ma_vec3f_len2(v));
51325}
51326
51327
51328
51329MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b)
51330{
51331 return ma_vec3f_len(ma_vec3f_sub(a, b));
51332}
51333
51334MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v)
51335{
51336 float invLen;
51337 float len2 = ma_vec3f_len2(v);
51338 if (len2 == 0) {
51339 return ma_vec3f_init_3f(0, 0, 0);
51340 }
51341
51342 invLen = ma_rsqrtf(len2);
51343 v.x *= invLen;
51344 v.y *= invLen;
51345 v.z *= invLen;
51346
51347 return v;
51348}
51349
51350MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b)
51351{
51352 return ma_vec3f_init_3f(
51353 a.y*b.z - a.z*b.y,
51354 a.z*b.x - a.x*b.z,
51355 a.x*b.y - a.y*b.x
51356 );
51357}
51358
51359
51360MA_API void ma_atomic_vec3f_init(ma_atomic_vec3f* v, ma_vec3f value)
51361{
51362 v->v = value;
51363 v->lock = 0; /* Important this is initialized to 0. */
51364}
51365
51366MA_API void ma_atomic_vec3f_set(ma_atomic_vec3f* v, ma_vec3f value)
51367{
51369 {
51370 v->v = value;
51371 }
51373}
51374
51375MA_API ma_vec3f ma_atomic_vec3f_get(ma_atomic_vec3f* v)
51376{
51377 ma_vec3f r;
51378
51380 {
51381 r = v->v;
51382 }
51384
51385 return r;
51386}
51387
51388
51389
51390static 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);
51391static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition);
51392
51393
51394#ifndef MA_DEFAULT_SPEED_OF_SOUND
51395#define MA_DEFAULT_SPEED_OF_SOUND 343.3f
51396#endif
51397
51398/*
51399These vectors represent the direction that speakers are facing from the center point. They're used
51400for panning in the spatializer. Must be normalized.
51401*/
51402static ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = {
51403 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_NONE */
51404 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_MONO */
51405 {-0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_LEFT */
51406 {+0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_RIGHT */
51407 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_FRONT_CENTER */
51408 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_LFE */
51409 {-0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_LEFT */
51410 {+0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_RIGHT */
51411 {-0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_LEFT_CENTER */
51412 {+0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_RIGHT_CENTER */
51413 { 0.0f, 0.0f, +1.0f }, /* MA_CHANNEL_BACK_CENTER */
51414 {-1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_LEFT */
51415 {+1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_RIGHT */
51416 { 0.0f, +1.0f, 0.0f }, /* MA_CHANNEL_TOP_CENTER */
51417 {-0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_LEFT */
51418 { 0.0f, +0.7071f, -0.7071f }, /* MA_CHANNEL_TOP_FRONT_CENTER */
51419 {+0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_RIGHT */
51420 {-0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_LEFT */
51421 { 0.0f, +0.7071f, +0.7071f }, /* MA_CHANNEL_TOP_BACK_CENTER */
51422 {+0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_RIGHT */
51423 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_0 */
51424 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_1 */
51425 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_2 */
51426 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_3 */
51427 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_4 */
51428 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_5 */
51429 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_6 */
51430 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_7 */
51431 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_8 */
51432 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_9 */
51433 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_10 */
51434 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_11 */
51435 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_12 */
51436 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_13 */
51437 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_14 */
51438 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_15 */
51439 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_16 */
51440 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_17 */
51441 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_18 */
51442 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_19 */
51443 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_20 */
51444 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_21 */
51445 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_22 */
51446 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_23 */
51447 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_24 */
51448 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_25 */
51449 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_26 */
51450 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_27 */
51451 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_28 */
51452 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_29 */
51453 { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_30 */
51454 { 0.0f, 0.0f, -1.0f } /* MA_CHANNEL_AUX_31 */
51455};
51456
51457static ma_vec3f ma_get_channel_direction(ma_channel channel)
51458{
51459 if (channel >= MA_CHANNEL_POSITION_COUNT) {
51460 return ma_vec3f_init_3f(0, 0, -1);
51461 } else {
51462 return g_maChannelDirections[channel];
51463 }
51464}
51465
51466
51467
51468static float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff)
51469{
51470 if (minDistance >= maxDistance) {
51471 return 1; /* To avoid division by zero. Do not attenuate. */
51472 }
51473
51474 return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance));
51475}
51476
51477static float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff)
51478{
51479 if (minDistance >= maxDistance) {
51480 return 1; /* To avoid division by zero. Do not attenuate. */
51481 }
51482
51483 return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance);
51484}
51485
51486static float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff)
51487{
51488 if (minDistance >= maxDistance) {
51489 return 1; /* To avoid division by zero. Do not attenuate. */
51490 }
51491
51492 return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff);
51493}
51494
51495
51496/*
51497Doppler Effect calculation taken from the OpenAL spec, with two main differences:
51498
51499 1) The source to listener vector will have already been calculated at an earlier step so we can
51500 just use that directly. We need only the position of the source relative to the origin.
51501
51502 2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight
51503 into the resampler directly.
51504*/
51505static float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor)
51506{
51507 float len;
51508 float vls;
51509 float vss;
51510
51511 len = ma_vec3f_len(relativePosition);
51512
51513 /*
51514 There's a case where the position of the source will be right on top of the listener in which
51515 case the length will be 0 and we'll end up with a division by zero. We can just return a ratio
51516 of 1.0 in this case. This is not considered in the OpenAL spec, but is necessary.
51517 */
51518 if (len == 0) {
51519 return 1.0;
51520 }
51521
51522 vls = ma_vec3f_dot(relativePosition, listenVelocity) / len;
51523 vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len;
51524
51525 vls = ma_min(vls, speedOfSound / dopplerFactor);
51526 vss = ma_min(vss, speedOfSound / dopplerFactor);
51527
51528 return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss);
51529}
51530
51531
51532static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channelCount)
51533{
51534 /*
51535 Special case for stereo. Want to default the left and right speakers to side left and side
51536 right so that they're facing directly down the X axis rather than slightly forward. Not
51537 doing this will result in sounds being quieter when behind the listener. This might
51538 actually be good for some scenarios, but I don't think it's an appropriate default because
51539 it can be a bit unexpected.
51540 */
51541 if (channelCount == 2) {
51542 pChannelMap[0] = MA_CHANNEL_SIDE_LEFT;
51543 pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT;
51544 } else {
51545 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
51546 }
51547}
51548
51549
51550MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut)
51551{
51552 ma_spatializer_listener_config config;
51553
51554 MA_ZERO_OBJECT(&config);
51555 config.channelsOut = channelsOut;
51556 config.pChannelMapOut = NULL;
51558 config.worldUp = ma_vec3f_init_3f(0, 1, 0);
51559 config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */
51560 config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */
51561 config.coneOuterGain = 0;
51562 config.speedOfSound = 343.3f; /* Same as OpenAL. Used for doppler effect. */
51563
51564 return config;
51565}
51566
51567
51568typedef struct
51569{
51570 size_t sizeInBytes;
51571 size_t channelMapOutOffset;
51572} ma_spatializer_listener_heap_layout;
51573
51574static ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout)
51575{
51576 MA_ASSERT(pHeapLayout != NULL);
51577
51578 MA_ZERO_OBJECT(pHeapLayout);
51579
51580 if (pConfig == NULL) {
51581 return MA_INVALID_ARGS;
51582 }
51583
51584 if (pConfig->channelsOut == 0) {
51585 return MA_INVALID_ARGS;
51586 }
51587
51588 pHeapLayout->sizeInBytes = 0;
51589
51590 /* Channel map. We always need this, even for passthroughs. */
51591 pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes;
51592 pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut);
51593
51594 return MA_SUCCESS;
51595}
51596
51597
51598MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes)
51599{
51600 ma_result result;
51601 ma_spatializer_listener_heap_layout heapLayout;
51602
51603 if (pHeapSizeInBytes == NULL) {
51604 return MA_INVALID_ARGS;
51605 }
51606
51607 *pHeapSizeInBytes = 0;
51608
51609 result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout);
51610 if (result != MA_SUCCESS) {
51611 return result;
51612 }
51613
51614 *pHeapSizeInBytes = heapLayout.sizeInBytes;
51615
51616 return MA_SUCCESS;
51617}
51618
51619MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener)
51620{
51621 ma_result result;
51622 ma_spatializer_listener_heap_layout heapLayout;
51623
51624 if (pListener == NULL) {
51625 return MA_INVALID_ARGS;
51626 }
51627
51628 MA_ZERO_OBJECT(pListener);
51629
51630 result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout);
51631 if (result != MA_SUCCESS) {
51632 return result;
51633 }
51634
51635 pListener->_pHeap = pHeap;
51636 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
51637
51638 pListener->config = *pConfig;
51639 ma_atomic_vec3f_init(&pListener->position, ma_vec3f_init_3f(0, 0, 0));
51640 ma_atomic_vec3f_init(&pListener->direction, ma_vec3f_init_3f(0, 0, -1));
51641 ma_atomic_vec3f_init(&pListener->velocity, ma_vec3f_init_3f(0, 0, 0));
51642 pListener->isEnabled = MA_TRUE;
51643
51644 /* Swap the forward direction if we're left handed (it was initialized based on right handed). */
51645 if (pListener->config.handedness == ma_handedness_left) {
51646 ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_listener_get_direction(pListener));
51647 ma_spatializer_listener_set_direction(pListener, negDir.x, negDir.y, negDir.z);
51648 }
51649
51650
51651 /* We must always have a valid channel map. */
51652 pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset);
51653
51654 /* Use a slightly different default channel map for stereo. */
51655 if (pConfig->pChannelMapOut == NULL) {
51656 ma_get_default_channel_map_for_spatializer(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->channelsOut);
51657 } else {
51659 }
51660
51661 return MA_SUCCESS;
51662}
51663
51664MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener)
51665{
51666 ma_result result;
51667 size_t heapSizeInBytes;
51668 void* pHeap;
51669
51670 result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes);
51671 if (result != MA_SUCCESS) {
51672 return result;
51673 }
51674
51675 if (heapSizeInBytes > 0) {
51676 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
51677 if (pHeap == NULL) {
51678 return MA_OUT_OF_MEMORY;
51679 }
51680 } else {
51681 pHeap = NULL;
51682 }
51683
51684 result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener);
51685 if (result != MA_SUCCESS) {
51686 ma_free(pHeap, pAllocationCallbacks);
51687 return result;
51688 }
51689
51690 pListener->_ownsHeap = MA_TRUE;
51691 return MA_SUCCESS;
51692}
51693
51694MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks)
51695{
51696 if (pListener == NULL) {
51697 return;
51698 }
51699
51700 if (pListener->_ownsHeap) {
51701 ma_free(pListener->_pHeap, pAllocationCallbacks);
51702 }
51703}
51704
51705MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener)
51706{
51707 if (pListener == NULL) {
51708 return NULL;
51709 }
51710
51711 return pListener->config.pChannelMapOut;
51712}
51713
51714MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
51715{
51716 if (pListener == NULL) {
51717 return;
51718 }
51719
51720 pListener->config.coneInnerAngleInRadians = innerAngleInRadians;
51721 pListener->config.coneOuterAngleInRadians = outerAngleInRadians;
51722 pListener->config.coneOuterGain = outerGain;
51723}
51724
51725MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
51726{
51727 if (pListener == NULL) {
51728 return;
51729 }
51730
51731 if (pInnerAngleInRadians != NULL) {
51732 *pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians;
51733 }
51734
51735 if (pOuterAngleInRadians != NULL) {
51736 *pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians;
51737 }
51738
51739 if (pOuterGain != NULL) {
51740 *pOuterGain = pListener->config.coneOuterGain;
51741 }
51742}
51743
51744MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z)
51745{
51746 if (pListener == NULL) {
51747 return;
51748 }
51749
51750 ma_atomic_vec3f_set(&pListener->position, ma_vec3f_init_3f(x, y, z));
51751}
51752
51753MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener)
51754{
51755 if (pListener == NULL) {
51756 return ma_vec3f_init_3f(0, 0, 0);
51757 }
51758
51759 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. */
51760}
51761
51762MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z)
51763{
51764 if (pListener == NULL) {
51765 return;
51766 }
51767
51768 ma_atomic_vec3f_set(&pListener->direction, ma_vec3f_init_3f(x, y, z));
51769}
51770
51771MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener)
51772{
51773 if (pListener == NULL) {
51774 return ma_vec3f_init_3f(0, 0, -1);
51775 }
51776
51777 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. */
51778}
51779
51780MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z)
51781{
51782 if (pListener == NULL) {
51783 return;
51784 }
51785
51786 ma_atomic_vec3f_set(&pListener->velocity, ma_vec3f_init_3f(x, y, z));
51787}
51788
51789MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener)
51790{
51791 if (pListener == NULL) {
51792 return ma_vec3f_init_3f(0, 0, 0);
51793 }
51794
51795 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. */
51796}
51797
51798MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound)
51799{
51800 if (pListener == NULL) {
51801 return;
51802 }
51803
51804 pListener->config.speedOfSound = speedOfSound;
51805}
51806
51807MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener)
51808{
51809 if (pListener == NULL) {
51810 return 0;
51811 }
51812
51813 return pListener->config.speedOfSound;
51814}
51815
51816MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z)
51817{
51818 if (pListener == NULL) {
51819 return;
51820 }
51821
51822 pListener->config.worldUp = ma_vec3f_init_3f(x, y, z);
51823}
51824
51825MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener)
51826{
51827 if (pListener == NULL) {
51828 return ma_vec3f_init_3f(0, 1, 0);
51829 }
51830
51831 return pListener->config.worldUp;
51832}
51833
51834MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled)
51835{
51836 if (pListener == NULL) {
51837 return;
51838 }
51839
51840 pListener->isEnabled = isEnabled;
51841}
51842
51843MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener)
51844{
51845 if (pListener == NULL) {
51846 return MA_FALSE;
51847 }
51848
51849 return pListener->isEnabled;
51850}
51851
51852
51853
51854
51855MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut)
51856{
51857 ma_spatializer_config config;
51858
51859 MA_ZERO_OBJECT(&config);
51860 config.channelsIn = channelsIn;
51861 config.channelsOut = channelsOut;
51862 config.pChannelMapIn = NULL;
51866 config.minGain = 0;
51867 config.maxGain = 1;
51868 config.minDistance = 1;
51869 config.maxDistance = MA_FLT_MAX;
51870 config.rolloff = 1;
51871 config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */
51872 config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */
51873 config.coneOuterGain = 0.0f;
51874 config.dopplerFactor = 1;
51876 config.minSpatializationChannelGain = 0.2f;
51877 config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */
51878
51879 return config;
51880}
51881
51882
51883static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig)
51884{
51885 MA_ASSERT(pConfig != NULL);
51886 return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames);
51887}
51888
51889static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig)
51890{
51891 MA_ASSERT(pConfig != NULL);
51892
51893 if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
51894 return MA_INVALID_ARGS;
51895 }
51896
51897 return MA_SUCCESS;
51898}
51899
51900typedef struct
51901{
51902 size_t sizeInBytes;
51903 size_t channelMapInOffset;
51904 size_t newChannelGainsOffset;
51905 size_t gainerOffset;
51906} ma_spatializer_heap_layout;
51907
51908static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout)
51909{
51910 ma_result result;
51911
51912 MA_ASSERT(pHeapLayout != NULL);
51913
51914 MA_ZERO_OBJECT(pHeapLayout);
51915
51916 if (pConfig == NULL) {
51917 return MA_INVALID_ARGS;
51918 }
51919
51920 result = ma_spatializer_validate_config(pConfig);
51921 if (result != MA_SUCCESS) {
51922 return result;
51923 }
51924
51925 pHeapLayout->sizeInBytes = 0;
51926
51927 /* Channel map. */
51928 pHeapLayout->channelMapInOffset = MA_SIZE_MAX; /* <-- MA_SIZE_MAX indicates no allocation necessary. */
51929 if (pConfig->pChannelMapIn != NULL) {
51930 pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes;
51931 pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn);
51932 }
51933
51934 /* New channel gains for output. */
51935 pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes;
51936 pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut);
51937
51938 /* Gainer. */
51939 {
51940 size_t gainerHeapSizeInBytes;
51941 ma_gainer_config gainerConfig;
51942
51943 gainerConfig = ma_spatializer_gainer_config_init(pConfig);
51944
51945 result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes);
51946 if (result != MA_SUCCESS) {
51947 return result;
51948 }
51949
51950 pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes;
51951 pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes);
51952 }
51953
51954 return MA_SUCCESS;
51955}
51956
51957MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes)
51958{
51959 ma_result result;
51960 ma_spatializer_heap_layout heapLayout;
51961
51962 if (pHeapSizeInBytes == NULL) {
51963 return MA_INVALID_ARGS;
51964 }
51965
51966 *pHeapSizeInBytes = 0; /* Safety. */
51967
51968 result = ma_spatializer_get_heap_layout(pConfig, &heapLayout);
51969 if (result != MA_SUCCESS) {
51970 return result;
51971 }
51972
51973 *pHeapSizeInBytes = heapLayout.sizeInBytes;
51974
51975 return MA_SUCCESS;
51976}
51977
51978
51979MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer)
51980{
51981 ma_result result;
51982 ma_spatializer_heap_layout heapLayout;
51983 ma_gainer_config gainerConfig;
51984
51985 if (pSpatializer == NULL) {
51986 return MA_INVALID_ARGS;
51987 }
51988
51989 MA_ZERO_OBJECT(pSpatializer);
51990
51991 if (pConfig == NULL || pHeap == NULL) {
51992 return MA_INVALID_ARGS;
51993 }
51994
51995 result = ma_spatializer_get_heap_layout(pConfig, &heapLayout);
51996 if (result != MA_SUCCESS) {
51997 return result;
51998 }
51999
52000 pSpatializer->_pHeap = pHeap;
52001 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
52002
52003 pSpatializer->channelsIn = pConfig->channelsIn;
52004 pSpatializer->channelsOut = pConfig->channelsOut;
52005 pSpatializer->attenuationModel = pConfig->attenuationModel;
52006 pSpatializer->positioning = pConfig->positioning;
52007 pSpatializer->handedness = pConfig->handedness;
52008 pSpatializer->minGain = pConfig->minGain;
52009 pSpatializer->maxGain = pConfig->maxGain;
52010 pSpatializer->minDistance = pConfig->minDistance;
52011 pSpatializer->maxDistance = pConfig->maxDistance;
52012 pSpatializer->rolloff = pConfig->rolloff;
52013 pSpatializer->coneInnerAngleInRadians = pConfig->coneInnerAngleInRadians;
52014 pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians;
52015 pSpatializer->coneOuterGain = pConfig->coneOuterGain;
52016 pSpatializer->dopplerFactor = pConfig->dopplerFactor;
52019 pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames;
52020 ma_atomic_vec3f_init(&pSpatializer->position, ma_vec3f_init_3f(0, 0, 0));
52021 ma_atomic_vec3f_init(&pSpatializer->direction, ma_vec3f_init_3f(0, 0, -1));
52022 ma_atomic_vec3f_init(&pSpatializer->velocity, ma_vec3f_init_3f(0, 0, 0));
52023 pSpatializer->dopplerPitch = 1;
52024
52025 /* Swap the forward direction if we're left handed (it was initialized based on right handed). */
52026 if (pSpatializer->handedness == ma_handedness_left) {
52027 ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_get_direction(pSpatializer));
52028 ma_spatializer_set_direction(pSpatializer, negDir.x, negDir.y, negDir.z);
52029 }
52030
52031 /* Channel map. This will be on the heap. */
52032 if (pConfig->pChannelMapIn != NULL) {
52033 pSpatializer->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset);
52034 ma_channel_map_copy_or_default(pSpatializer->pChannelMapIn, pSpatializer->channelsIn, pConfig->pChannelMapIn, pSpatializer->channelsIn);
52035 }
52036
52037 /* New channel gains for output channels. */
52038 pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset);
52039
52040 /* Gainer. */
52041 gainerConfig = ma_spatializer_gainer_config_init(pConfig);
52042
52043 result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer);
52044 if (result != MA_SUCCESS) {
52045 return result; /* Failed to initialize the gainer. */
52046 }
52047
52048 return MA_SUCCESS;
52049}
52050
52051MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer)
52052{
52053 ma_result result;
52054 size_t heapSizeInBytes;
52055 void* pHeap;
52056
52057 /* We'll need a heap allocation to retrieve the size. */
52058 result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes);
52059 if (result != MA_SUCCESS) {
52060 return result;
52061 }
52062
52063 if (heapSizeInBytes > 0) {
52064 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
52065 if (pHeap == NULL) {
52066 return MA_OUT_OF_MEMORY;
52067 }
52068 } else {
52069 pHeap = NULL;
52070 }
52071
52072 result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer);
52073 if (result != MA_SUCCESS) {
52074 ma_free(pHeap, pAllocationCallbacks);
52075 return result;
52076 }
52077
52078 pSpatializer->_ownsHeap = MA_TRUE;
52079 return MA_SUCCESS;
52080}
52081
52082MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks)
52083{
52084 if (pSpatializer == NULL) {
52085 return;
52086 }
52087
52088 ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks);
52089
52090 if (pSpatializer->_ownsHeap) {
52091 ma_free(pSpatializer->_pHeap, pAllocationCallbacks);
52092 }
52093}
52094
52095static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain)
52096{
52097 /*
52098 Angular attenuation.
52099
52100 Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure
52101 this out for ourselves at the expense of possibly being inconsistent with other implementations.
52102
52103 To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We
52104 just need to get the direction from the source to the listener and then do a dot product against that and the
52105 direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer
52106 angles. If the dot product is greater than the outer angle, we just use coneOuterGain. If it's less than
52107 the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain.
52108 */
52109 if (coneInnerAngleInRadians < 6.283185f) {
52110 float angularGain = 1;
52111 float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f);
52112 float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f);
52113 float d;
52114
52115 d = ma_vec3f_dot(dirA, dirB);
52116
52117 if (d > cutoffInner) {
52118 /* It's inside the inner angle. */
52119 angularGain = 1;
52120 } else {
52121 /* It's outside the inner angle. */
52122 if (d > cutoffOuter) {
52123 /* It's between the inner and outer angle. We need to linearly interpolate between 1 and coneOuterGain. */
52124 angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter));
52125 } else {
52126 /* It's outside the outer angle. */
52127 angularGain = coneOuterGain;
52128 }
52129 }
52130
52131 /*printf("d = %f; cutoffInner = %f; cutoffOuter = %f; angularGain = %f\n", d, cutoffInner, cutoffOuter, angularGain);*/
52132 return angularGain;
52133 } else {
52134 /* Inner angle is 360 degrees so no need to do any attenuation. */
52135 return 1;
52136 }
52137}
52138
52139MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
52140{
52141 ma_channel* pChannelMapIn = pSpatializer->pChannelMapIn;
52142 ma_channel* pChannelMapOut = pListener->config.pChannelMapOut;
52143
52144 if (pSpatializer == NULL) {
52145 return MA_INVALID_ARGS;
52146 }
52147
52148 /* If we're not spatializing we need to run an optimized path. */
52149 if (ma_atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) {
52150 if (ma_spatializer_listener_is_enabled(pListener)) {
52151 /* No attenuation is required, but we'll need to do some channel conversion. */
52152 if (pSpatializer->channelsIn == pSpatializer->channelsOut) {
52153 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->channelsIn);
52154 } else {
52155 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. */
52156 }
52157 } else {
52158 /* The listener is disabled. Output silence. */
52159 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut);
52160 }
52161
52162 /*
52163 We're not doing attenuation so don't bother with doppler for now. I'm not sure if this is
52164 the correct thinking so might need to review this later.
52165 */
52166 pSpatializer->dopplerPitch = 1;
52167 } else {
52168 /*
52169 Let's first determine which listener the sound is closest to. Need to keep in mind that we
52170 might not have a world or any listeners, in which case we just spatializer based on the
52171 listener being positioned at the origin (0, 0, 0).
52172 */
52173 ma_vec3f relativePosNormalized;
52174 ma_vec3f relativePos; /* The position relative to the listener. */
52175 ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */
52176 ma_vec3f listenerVel; /* The velocity of the listener. For doppler pitch calculation. */
52177 float speedOfSound;
52178 float distance = 0;
52179 float gain = 1;
52180 ma_uint32 iChannel;
52181 const ma_uint32 channelsOut = pSpatializer->channelsOut;
52182 const ma_uint32 channelsIn = pSpatializer->channelsIn;
52183 float minDistance = ma_spatializer_get_min_distance(pSpatializer);
52184 float maxDistance = ma_spatializer_get_max_distance(pSpatializer);
52185 float rolloff = ma_spatializer_get_rolloff(pSpatializer);
52186 float dopplerFactor = ma_spatializer_get_doppler_factor(pSpatializer);
52187
52188 /*
52189 We'll need the listener velocity for doppler pitch calculations. The speed of sound is
52190 defined by the listener, so we'll grab that here too.
52191 */
52192 if (pListener != NULL) {
52193 listenerVel = ma_spatializer_listener_get_velocity(pListener);
52194 speedOfSound = pListener->config.speedOfSound;
52195 } else {
52196 listenerVel = ma_vec3f_init_3f(0, 0, 0);
52197 speedOfSound = MA_DEFAULT_SPEED_OF_SOUND;
52198 }
52199
52200 if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) {
52201 /* There's no listener or we're using relative positioning. */
52202 relativePos = ma_spatializer_get_position(pSpatializer);
52203 relativeDir = ma_spatializer_get_direction(pSpatializer);
52204 } else {
52205 /*
52206 We've found a listener and we're using absolute positioning. We need to transform the
52207 sound's position and direction so that it's relative to listener. Later on we'll use
52208 this for determining the factors to apply to each channel to apply the panning effect.
52209 */
52210 ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir);
52211 }
52212
52213 distance = ma_vec3f_len(relativePos);
52214
52215 /* We've gathered the data, so now we can apply some spatialization. */
52216 switch (ma_spatializer_get_attenuation_model(pSpatializer)) {
52218 {
52219 gain = ma_attenuation_inverse(distance, minDistance, maxDistance, rolloff);
52220 } break;
52222 {
52223 gain = ma_attenuation_linear(distance, minDistance, maxDistance, rolloff);
52224 } break;
52226 {
52227 gain = ma_attenuation_exponential(distance, minDistance, maxDistance, rolloff);
52228 } break;
52230 default:
52231 {
52232 gain = 1;
52233 } break;
52234 }
52235
52236 /* Normalize the position. */
52237 if (distance > 0.001f) {
52238 float distanceInv = 1/distance;
52239 relativePosNormalized = relativePos;
52240 relativePosNormalized.x *= distanceInv;
52241 relativePosNormalized.y *= distanceInv;
52242 relativePosNormalized.z *= distanceInv;
52243 } else {
52244 distance = 0;
52245 relativePosNormalized = ma_vec3f_init_3f(0, 0, 0);
52246 }
52247
52248 /*
52249 Angular attenuation.
52250
52251 Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure
52252 this out for ourselves at the expense of possibly being inconsistent with other implementations.
52253
52254 To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We
52255 just need to get the direction from the source to the listener and then do a dot product against that and the
52256 direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer
52257 angles. If the dot product is greater than the outer angle, we just use coneOuterGain. If it's less than
52258 the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain.
52259 */
52260 if (distance > 0) {
52261 /* Source angular gain. */
52262 float spatializerConeInnerAngle;
52263 float spatializerConeOuterAngle;
52264 float spatializerConeOuterGain;
52265 ma_spatializer_get_cone(pSpatializer, &spatializerConeInnerAngle, &spatializerConeOuterAngle, &spatializerConeOuterGain);
52266
52267 gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), spatializerConeInnerAngle, spatializerConeOuterAngle, spatializerConeOuterGain);
52268
52269 /*
52270 We're supporting angular gain on the listener as well for those who want to reduce the volume of sounds that
52271 are positioned behind the listener. On default settings, this will have no effect.
52272 */
52273 if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) {
52274 ma_vec3f listenerDirection;
52275 float listenerInnerAngle;
52276 float listenerOuterAngle;
52277 float listenerOuterGain;
52278
52279 if (pListener->config.handedness == ma_handedness_right) {
52280 listenerDirection = ma_vec3f_init_3f(0, 0, -1);
52281 } else {
52282 listenerDirection = ma_vec3f_init_3f(0, 0, +1);
52283 }
52284
52285 listenerInnerAngle = pListener->config.coneInnerAngleInRadians;
52286 listenerOuterAngle = pListener->config.coneOuterAngleInRadians;
52287 listenerOuterGain = pListener->config.coneOuterGain;
52288
52289 gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain);
52290 }
52291 } else {
52292 /* The sound is right on top of the listener. Don't do any angular attenuation. */
52293 }
52294
52295
52296 /* Clamp the gain. */
52297 gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer));
52298
52299 /*
52300 The gain needs to be applied per-channel here. The spatialization code below will be changing the per-channel
52301 gains which will then eventually be passed into the gainer which will deal with smoothing the gain transitions
52302 to avoid harsh changes in gain.
52303 */
52304 for (iChannel = 0; iChannel < channelsOut; iChannel += 1) {
52305 pSpatializer->pNewChannelGainsOut[iChannel] = gain;
52306 }
52307
52308 /*
52309 Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore
52310 the whole section of code here because we need to update some internal spatialization state.
52311 */
52312 if (ma_spatializer_listener_is_enabled(pListener)) {
52313 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);
52314 } else {
52315 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut);
52316 }
52317
52318
52319 /*
52320 Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for
52321 when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the
52322 gain to the final output.
52323 */
52324 /*printf("distance=%f; gain=%f\n", distance, gain);*/
52325
52326 /* We must have a valid channel map here to ensure we spatialize properly. */
52327 MA_ASSERT(pChannelMapOut != NULL);
52328
52329 /*
52330 We're not converting to mono so we'll want to apply some panning. This is where the feeling of something being
52331 to the left, right, infront or behind the listener is calculated. I'm just using a basic model here. Note that
52332 the code below is not based on any specific algorithm. I'm just implementing this off the top of my head and
52333 seeing how it goes. There might be better ways to do this.
52334
52335 To determine the direction of the sound relative to a speaker I'm using dot products. Each speaker is given a
52336 direction. For example, the left channel in a stereo system will be -1 on the X axis and the right channel will
52337 be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized
52338 position of the sound.
52339 */
52340
52341 /*
52342 Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's
52343 relation to the direction of the channel.
52344 */
52345 if (distance > 0) {
52346 ma_vec3f unitPos = relativePos;
52347 float distanceInv = 1/distance;
52348 unitPos.x *= distanceInv;
52349 unitPos.y *= distanceInv;
52350 unitPos.z *= distanceInv;
52351
52352 for (iChannel = 0; iChannel < channelsOut; iChannel += 1) {
52353 ma_channel channelOut;
52354 float d;
52355 float dMin;
52356
52357 channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel);
52358 if (ma_is_spatial_channel_position(channelOut)) {
52359 d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), ma_spatializer_get_directional_attenuation_factor(pSpatializer));
52360 } else {
52361 d = 1; /* It's not a spatial channel so there's no real notion of direction. */
52362 }
52363
52364 /*
52365 In my testing, if the panning effect is too aggressive it makes spatialization feel uncomfortable.
52366 The "dMin" variable below is used to control the aggressiveness of the panning effect. When set to
52367 0, panning will be most extreme and any sounds that are positioned on the opposite side of the
52368 speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it
52369 doesn't even remotely represent the real world at all because sounds that come from your right side
52370 are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at
52371 all, which is also not ideal. By setting it to something greater than 0, the spatialization effect
52372 becomes much less dramatic and a lot more bearable.
52373
52374 Summary: 0 = more extreme panning; 1 = no panning.
52375 */
52376 dMin = pSpatializer->minSpatializationChannelGain;
52377
52378 /*
52379 At this point, "d" will be positive if the sound is on the same side as the channel and negative if
52380 it's on the opposite side. It will be in the range of -1..1. There's two ways I can think of to
52381 calculate a panning value. The first is to simply convert it to 0..1, however this has a problem
52382 which I'm not entirely happy with. Considering a stereo system, when a sound is positioned right
52383 in front of the listener it'll result in each speaker getting a gain of 0.5. I don't know if I like
52384 the idea of having a scaling factor of 0.5 being applied to a sound when it's sitting right in front
52385 of the listener. I would intuitively expect that to be played at full volume, or close to it.
52386
52387 The second idea I think of is to only apply a reduction in gain when the sound is on the opposite
52388 side of the speaker. That is, reduce the gain only when the dot product is negative. The problem
52389 with this is that there will not be any attenuation as the sound sweeps around the 180 degrees
52390 where the dot product is positive. The idea with this option is that you leave the gain at 1 when
52391 the sound is being played on the same side as the speaker and then you just reduce the volume when
52392 the sound is on the other side.
52393
52394 The summarize, I think the first option should give a better sense of spatialization, but the second
52395 option is better for preserving the sound's power.
52396
52397 UPDATE: In my testing, I find the first option to sound better. You can feel the sense of space a
52398 bit better, but you can also hear the reduction in volume when it's right in front.
52399 */
52400 #if 1
52401 {
52402 /*
52403 Scale the dot product from -1..1 to 0..1. Will result in a sound directly in front losing power
52404 by being played at 0.5 gain.
52405 */
52406 d = (d + 1) * 0.5f; /* -1..1 to 0..1 */
52407 d = ma_max(d, dMin);
52408 pSpatializer->pNewChannelGainsOut[iChannel] *= d;
52409 }
52410 #else
52411 {
52412 /*
52413 Only reduce the volume of the sound if it's on the opposite side. This path keeps the volume more
52414 consistent, but comes at the expense of a worse sense of space and positioning.
52415 */
52416 if (d < 0) {
52417 d += 1; /* Move into the positive range. */
52418 d = ma_max(d, dMin);
52419 channelGainsOut[iChannel] *= d;
52420 }
52421 }
52422 #endif
52423 }
52424 } else {
52425 /* Assume the sound is right on top of us. Don't do any panning. */
52426 }
52427
52428 /* 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. */
52429 ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut);
52430 ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount);
52431
52432 /*
52433 Before leaving we'll want to update our doppler pitch so that the caller can apply some
52434 pitch shifting if they desire. Note that we need to negate the relative position here
52435 because the doppler calculation needs to be source-to-listener, but ours is listener-to-
52436 source.
52437 */
52438 if (dopplerFactor > 0) {
52439 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);
52440 } else {
52441 pSpatializer->dopplerPitch = 1;
52442 }
52443 }
52444
52445 return MA_SUCCESS;
52446}
52447
52448MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume)
52449{
52450 if (pSpatializer == NULL) {
52451 return MA_INVALID_ARGS;
52452 }
52453
52454 return ma_gainer_set_master_volume(&pSpatializer->gainer, volume);
52455}
52456
52457MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume)
52458{
52459 if (pSpatializer == NULL) {
52460 return MA_INVALID_ARGS;
52461 }
52462
52463 return ma_gainer_get_master_volume(&pSpatializer->gainer, pVolume);
52464}
52465
52466MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer)
52467{
52468 if (pSpatializer == NULL) {
52469 return 0;
52470 }
52471
52472 return pSpatializer->channelsIn;
52473}
52474
52475MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer)
52476{
52477 if (pSpatializer == NULL) {
52478 return 0;
52479 }
52480
52481 return pSpatializer->channelsOut;
52482}
52483
52484MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel)
52485{
52486 if (pSpatializer == NULL) {
52487 return;
52488 }
52489
52490 ma_atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel);
52491}
52492
52493MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer)
52494{
52495 if (pSpatializer == NULL) {
52497 }
52498
52499 return (ma_attenuation_model)ma_atomic_load_i32(&pSpatializer->attenuationModel);
52500}
52501
52502MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning)
52503{
52504 if (pSpatializer == NULL) {
52505 return;
52506 }
52507
52508 ma_atomic_exchange_i32(&pSpatializer->positioning, positioning);
52509}
52510
52511MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer)
52512{
52513 if (pSpatializer == NULL) {
52515 }
52516
52517 return (ma_positioning)ma_atomic_load_i32(&pSpatializer->positioning);
52518}
52519
52520MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff)
52521{
52522 if (pSpatializer == NULL) {
52523 return;
52524 }
52525
52526 ma_atomic_exchange_f32(&pSpatializer->rolloff, rolloff);
52527}
52528
52529MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer)
52530{
52531 if (pSpatializer == NULL) {
52532 return 0;
52533 }
52534
52535 return ma_atomic_load_f32(&pSpatializer->rolloff);
52536}
52537
52538MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain)
52539{
52540 if (pSpatializer == NULL) {
52541 return;
52542 }
52543
52544 ma_atomic_exchange_f32(&pSpatializer->minGain, minGain);
52545}
52546
52547MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer)
52548{
52549 if (pSpatializer == NULL) {
52550 return 0;
52551 }
52552
52553 return ma_atomic_load_f32(&pSpatializer->minGain);
52554}
52555
52556MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain)
52557{
52558 if (pSpatializer == NULL) {
52559 return;
52560 }
52561
52562 ma_atomic_exchange_f32(&pSpatializer->maxGain, maxGain);
52563}
52564
52565MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer)
52566{
52567 if (pSpatializer == NULL) {
52568 return 0;
52569 }
52570
52571 return ma_atomic_load_f32(&pSpatializer->maxGain);
52572}
52573
52574MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance)
52575{
52576 if (pSpatializer == NULL) {
52577 return;
52578 }
52579
52580 ma_atomic_exchange_f32(&pSpatializer->minDistance, minDistance);
52581}
52582
52583MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer)
52584{
52585 if (pSpatializer == NULL) {
52586 return 0;
52587 }
52588
52589 return ma_atomic_load_f32(&pSpatializer->minDistance);
52590}
52591
52592MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance)
52593{
52594 if (pSpatializer == NULL) {
52595 return;
52596 }
52597
52598 ma_atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance);
52599}
52600
52601MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer)
52602{
52603 if (pSpatializer == NULL) {
52604 return 0;
52605 }
52606
52607 return ma_atomic_load_f32(&pSpatializer->maxDistance);
52608}
52609
52610MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
52611{
52612 if (pSpatializer == NULL) {
52613 return;
52614 }
52615
52616 ma_atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians);
52617 ma_atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians);
52618 ma_atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain);
52619}
52620
52621MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
52622{
52623 if (pSpatializer == NULL) {
52624 return;
52625 }
52626
52627 if (pInnerAngleInRadians != NULL) {
52628 *pInnerAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneInnerAngleInRadians);
52629 }
52630
52631 if (pOuterAngleInRadians != NULL) {
52632 *pOuterAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneOuterAngleInRadians);
52633 }
52634
52635 if (pOuterGain != NULL) {
52636 *pOuterGain = ma_atomic_load_f32(&pSpatializer->coneOuterGain);
52637 }
52638}
52639
52640MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor)
52641{
52642 if (pSpatializer == NULL) {
52643 return;
52644 }
52645
52646 ma_atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor);
52647}
52648
52649MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer)
52650{
52651 if (pSpatializer == NULL) {
52652 return 1;
52653 }
52654
52655 return ma_atomic_load_f32(&pSpatializer->dopplerFactor);
52656}
52657
52658MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor)
52659{
52660 if (pSpatializer == NULL) {
52661 return;
52662 }
52663
52664 ma_atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor);
52665}
52666
52667MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer)
52668{
52669 if (pSpatializer == NULL) {
52670 return 1;
52671 }
52672
52673 return ma_atomic_load_f32(&pSpatializer->directionalAttenuationFactor);
52674}
52675
52676MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z)
52677{
52678 if (pSpatializer == NULL) {
52679 return;
52680 }
52681
52682 ma_atomic_vec3f_set(&pSpatializer->position, ma_vec3f_init_3f(x, y, z));
52683}
52684
52685MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer)
52686{
52687 if (pSpatializer == NULL) {
52688 return ma_vec3f_init_3f(0, 0, 0);
52689 }
52690
52691 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. */
52692}
52693
52694MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z)
52695{
52696 if (pSpatializer == NULL) {
52697 return;
52698 }
52699
52700 ma_atomic_vec3f_set(&pSpatializer->direction, ma_vec3f_init_3f(x, y, z));
52701}
52702
52703MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer)
52704{
52705 if (pSpatializer == NULL) {
52706 return ma_vec3f_init_3f(0, 0, -1);
52707 }
52708
52709 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. */
52710}
52711
52712MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z)
52713{
52714 if (pSpatializer == NULL) {
52715 return;
52716 }
52717
52718 ma_atomic_vec3f_set(&pSpatializer->velocity, ma_vec3f_init_3f(x, y, z));
52719}
52720
52721MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer)
52722{
52723 if (pSpatializer == NULL) {
52724 return ma_vec3f_init_3f(0, 0, 0);
52725 }
52726
52727 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. */
52728}
52729
52730MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir)
52731{
52732 if (pRelativePos != NULL) {
52733 pRelativePos->x = 0;
52734 pRelativePos->y = 0;
52735 pRelativePos->z = 0;
52736 }
52737
52738 if (pRelativeDir != NULL) {
52739 pRelativeDir->x = 0;
52740 pRelativeDir->y = 0;
52741 pRelativeDir->z = -1;
52742 }
52743
52744 if (pSpatializer == NULL) {
52745 return;
52746 }
52747
52748 if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) {
52749 /* There's no listener or we're using relative positioning. */
52750 if (pRelativePos != NULL) {
52751 *pRelativePos = ma_spatializer_get_position(pSpatializer);
52752 }
52753 if (pRelativeDir != NULL) {
52754 *pRelativeDir = ma_spatializer_get_direction(pSpatializer);
52755 }
52756 } else {
52757 ma_vec3f spatializerPosition;
52758 ma_vec3f spatializerDirection;
52759 ma_vec3f listenerPosition;
52760 ma_vec3f listenerDirection;
52761 ma_vec3f v;
52762 ma_vec3f axisX;
52763 ma_vec3f axisY;
52764 ma_vec3f axisZ;
52765 float m[4][4];
52766
52767 spatializerPosition = ma_spatializer_get_position(pSpatializer);
52768 spatializerDirection = ma_spatializer_get_direction(pSpatializer);
52769 listenerPosition = ma_spatializer_listener_get_position(pListener);
52770 listenerDirection = ma_spatializer_listener_get_direction(pListener);
52771
52772 /*
52773 We need to calculate the right vector from our forward and up vectors. This is done with
52774 a cross product.
52775 */
52776 axisZ = ma_vec3f_normalize(listenerDirection); /* Normalization required here because we can't trust the caller. */
52777 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. */
52778
52779 /*
52780 The calculation of axisX above can result in a zero-length vector if the listener is
52781 looking straight up on the Y axis. We'll need to fall back to a +X in this case so that
52782 the calculations below don't fall apart. This is where a quaternion based listener and
52783 sound orientation would come in handy.
52784 */
52785 if (ma_vec3f_len2(axisX) == 0) {
52786 axisX = ma_vec3f_init_3f(1, 0, 0);
52787 }
52788
52789 axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */
52790
52791 /*
52792 We need to swap the X axis if we're left handed because otherwise the cross product above
52793 will have resulted in it pointing in the wrong direction (right handed was assumed in the
52794 cross products above).
52795 */
52796 if (pListener->config.handedness == ma_handedness_left) {
52797 axisX = ma_vec3f_neg(axisX);
52798 }
52799
52800 /* Lookat. */
52801 m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, listenerPosition);
52802 m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, listenerPosition);
52803 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);
52804 m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1;
52805
52806 /*
52807 Multiply the lookat matrix by the spatializer position to transform it to listener
52808 space. This allows calculations to work based on the sound being relative to the
52809 origin which makes things simpler.
52810 */
52811 if (pRelativePos != NULL) {
52812 v = spatializerPosition;
52813 pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1;
52814 pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1;
52815 pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1;
52816 }
52817
52818 /*
52819 The direction of the sound needs to also be transformed so that it's relative to the
52820 rotation of the listener.
52821 */
52822 if (pRelativeDir != NULL) {
52823 v = spatializerDirection;
52824 pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z;
52825 pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z;
52826 pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z;
52827 }
52828 }
52829}
52830
52831
52832
52833
52834/**************************************************************************************************************************************************************
52835
52836Resampling
52837
52838**************************************************************************************************************************************************************/
52839MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
52840{
52841 ma_linear_resampler_config config;
52842 MA_ZERO_OBJECT(&config);
52843 config.format = format;
52844 config.channels = channels;
52845 config.sampleRateIn = sampleRateIn;
52846 config.sampleRateOut = sampleRateOut;
52847 config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
52848 config.lpfNyquistFactor = 1;
52849
52850 return config;
52851}
52852
52853
52854typedef struct
52855{
52856 size_t sizeInBytes;
52857 size_t x0Offset;
52858 size_t x1Offset;
52859 size_t lpfOffset;
52860} ma_linear_resampler_heap_layout;
52861
52862
52863static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut)
52864{
52865 /*
52866 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
52867 be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate.
52868 */
52869 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. */
52870 ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut;
52871
52872 pResampler->inTimeFrac =
52873 (oldRateTimeWhole * newSampleRateOut) +
52874 ((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut);
52875
52876 /* Make sure the fractional part is less than the output sample rate. */
52877 pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut;
52878 pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut;
52879}
52880
52881static 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)
52882{
52883 ma_result result;
52884 ma_uint32 gcf;
52885 ma_uint32 lpfSampleRate;
52886 double lpfCutoffFrequency;
52887 ma_lpf_config lpfConfig;
52888 ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */
52889
52890 if (pResampler == NULL) {
52891 return MA_INVALID_ARGS;
52892 }
52893
52894 if (sampleRateIn == 0 || sampleRateOut == 0) {
52895 return MA_INVALID_ARGS;
52896 }
52897
52898 oldSampleRateOut = pResampler->config.sampleRateOut;
52899
52900 pResampler->config.sampleRateIn = sampleRateIn;
52901 pResampler->config.sampleRateOut = sampleRateOut;
52902
52903 /* Simplify the sample rate. */
52904 gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut);
52905 pResampler->config.sampleRateIn /= gcf;
52906 pResampler->config.sampleRateOut /= gcf;
52907
52908 /* Always initialize the low-pass filter, even when the order is 0. */
52909 if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) {
52910 return MA_INVALID_ARGS;
52911 }
52912
52913 lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut));
52914 lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor);
52915
52916 lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder);
52917
52918 /*
52919 If the resampler is already initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames
52920 getting cleared. Instead we re-initialize the filter which will maintain any cached frames.
52921 */
52922 if (isResamplerAlreadyInitialized) {
52923 result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf);
52924 } else {
52925 result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf);
52926 }
52927
52928 if (result != MA_SUCCESS) {
52929 return result;
52930 }
52931
52932
52933 pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut;
52934 pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut;
52935
52936 /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */
52937 ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut);
52938
52939 return MA_SUCCESS;
52940}
52941
52942static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout)
52943{
52944 MA_ASSERT(pHeapLayout != NULL);
52945
52946 MA_ZERO_OBJECT(pHeapLayout);
52947
52948 if (pConfig == NULL) {
52949 return MA_INVALID_ARGS;
52950 }
52951
52952 if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
52953 return MA_INVALID_ARGS;
52954 }
52955
52956 if (pConfig->channels == 0) {
52957 return MA_INVALID_ARGS;
52958 }
52959
52960 pHeapLayout->sizeInBytes = 0;
52961
52962 /* x0 */
52963 pHeapLayout->x0Offset = pHeapLayout->sizeInBytes;
52964 if (pConfig->format == ma_format_f32) {
52965 pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
52966 } else {
52967 pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels;
52968 }
52969
52970 /* x1 */
52971 pHeapLayout->x1Offset = pHeapLayout->sizeInBytes;
52972 if (pConfig->format == ma_format_f32) {
52973 pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
52974 } else {
52975 pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels;
52976 }
52977
52978 /* LPF */
52979 pHeapLayout->lpfOffset = ma_align_64(pHeapLayout->sizeInBytes);
52980 {
52981 ma_result result;
52982 size_t lpfHeapSizeInBytes;
52983 ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder); /* Sample rate and cutoff frequency do not matter. */
52984
52985 result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes);
52986 if (result != MA_SUCCESS) {
52987 return result;
52988 }
52989
52990 pHeapLayout->sizeInBytes += lpfHeapSizeInBytes;
52991 }
52992
52993 /* Make sure allocation size is aligned. */
52994 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
52995
52996 return MA_SUCCESS;
52997}
52998
52999MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes)
53000{
53001 ma_result result;
53002 ma_linear_resampler_heap_layout heapLayout;
53003
53004 if (pHeapSizeInBytes == NULL) {
53005 return MA_INVALID_ARGS;
53006 }
53007
53008 *pHeapSizeInBytes = 0;
53009
53010 result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout);
53011 if (result != MA_SUCCESS) {
53012 return result;
53013 }
53014
53015 *pHeapSizeInBytes = heapLayout.sizeInBytes;
53016
53017 return MA_SUCCESS;
53018}
53019
53020MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler)
53021{
53022 ma_result result;
53023 ma_linear_resampler_heap_layout heapLayout;
53024
53025 if (pResampler == NULL) {
53026 return MA_INVALID_ARGS;
53027 }
53028
53029 MA_ZERO_OBJECT(pResampler);
53030
53031 result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout);
53032 if (result != MA_SUCCESS) {
53033 return result;
53034 }
53035
53036 pResampler->config = *pConfig;
53037
53038 pResampler->_pHeap = pHeap;
53039 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
53040
53041 if (pConfig->format == ma_format_f32) {
53042 pResampler->x0.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x0Offset);
53043 pResampler->x1.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x1Offset);
53044 } else {
53045 pResampler->x0.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x0Offset);
53046 pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset);
53047 }
53048
53049 /* Setting the rate will set up the filter and time advances for us. */
53050 result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE);
53051 if (result != MA_SUCCESS) {
53052 return result;
53053 }
53054
53055 pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */
53056 pResampler->inTimeFrac = 0;
53057
53058 return MA_SUCCESS;
53059}
53060
53061MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler)
53062{
53063 ma_result result;
53064 size_t heapSizeInBytes;
53065 void* pHeap;
53066
53067 result = ma_linear_resampler_get_heap_size(pConfig, &heapSizeInBytes);
53068 if (result != MA_SUCCESS) {
53069 return result;
53070 }
53071
53072 if (heapSizeInBytes > 0) {
53073 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
53074 if (pHeap == NULL) {
53075 return MA_OUT_OF_MEMORY;
53076 }
53077 } else {
53078 pHeap = NULL;
53079 }
53080
53081 result = ma_linear_resampler_init_preallocated(pConfig, pHeap, pResampler);
53082 if (result != MA_SUCCESS) {
53083 ma_free(pHeap, pAllocationCallbacks);
53084 return result;
53085 }
53086
53087 pResampler->_ownsHeap = MA_TRUE;
53088 return MA_SUCCESS;
53089}
53090
53091MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks)
53092{
53093 if (pResampler == NULL) {
53094 return;
53095 }
53096
53097 ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks);
53098
53099 if (pResampler->_ownsHeap) {
53100 ma_free(pResampler->_pHeap, pAllocationCallbacks);
53101 }
53102}
53103
53104static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift)
53105{
53106 ma_int32 b;
53107 ma_int32 c;
53108 ma_int32 r;
53109
53110 MA_ASSERT(a <= (1<<shift));
53111
53112 b = x * ((1<<shift) - a);
53113 c = y * a;
53114 r = b + c;
53115
53116 return (ma_int16)(r >> shift);
53117}
53118
53119static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut)
53120{
53121 ma_uint32 c;
53122 ma_uint32 a;
53123 const ma_uint32 channels = pResampler->config.channels;
53124 const ma_uint32 shift = 12;
53125
53126 MA_ASSERT(pResampler != NULL);
53127 MA_ASSERT(pFrameOut != NULL);
53128
53129 a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut;
53130
53131 MA_ASSUME(channels > 0);
53132 for (c = 0; c < channels; c += 1) {
53133 ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift);
53134 pFrameOut[c] = s;
53135 }
53136}
53137
53138
53139static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut)
53140{
53141 ma_uint32 c;
53142 float a;
53143 const ma_uint32 channels = pResampler->config.channels;
53144
53145 MA_ASSERT(pResampler != NULL);
53146 MA_ASSERT(pFrameOut != NULL);
53147
53148 a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut;
53149
53150 MA_ASSUME(channels > 0);
53151 for (c = 0; c < channels; c += 1) {
53152 float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a);
53153 pFrameOut[c] = s;
53154 }
53155}
53156
53157static 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)
53158{
53159 const ma_int16* pFramesInS16;
53160 /* */ ma_int16* pFramesOutS16;
53161 ma_uint64 frameCountIn;
53162 ma_uint64 frameCountOut;
53163 ma_uint64 framesProcessedIn;
53164 ma_uint64 framesProcessedOut;
53165
53166 MA_ASSERT(pResampler != NULL);
53167 MA_ASSERT(pFrameCountIn != NULL);
53168 MA_ASSERT(pFrameCountOut != NULL);
53169
53170 pFramesInS16 = (const ma_int16*)pFramesIn;
53171 pFramesOutS16 = ( ma_int16*)pFramesOut;
53172 frameCountIn = *pFrameCountIn;
53173 frameCountOut = *pFrameCountOut;
53174 framesProcessedIn = 0;
53175 framesProcessedOut = 0;
53176
53177 while (framesProcessedOut < frameCountOut) {
53178 /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
53179 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
53180 ma_uint32 iChannel;
53181
53182 if (pFramesInS16 != NULL) {
53183 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
53184 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
53185 pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
53186 }
53187 pFramesInS16 += pResampler->config.channels;
53188 } else {
53189 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
53190 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
53191 pResampler->x1.s16[iChannel] = 0;
53192 }
53193 }
53194
53195 /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */
53196 if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) {
53197 ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16);
53198 }
53199
53200 framesProcessedIn += 1;
53201 pResampler->inTimeInt -= 1;
53202 }
53203
53204 if (pResampler->inTimeInt > 0) {
53205 break; /* Ran out of input data. */
53206 }
53207
53208 /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
53209 if (pFramesOutS16 != NULL) {
53210 MA_ASSERT(pResampler->inTimeInt == 0);
53211 ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
53212
53213 pFramesOutS16 += pResampler->config.channels;
53214 }
53215
53216 framesProcessedOut += 1;
53217
53218 /* Advance time forward. */
53219 pResampler->inTimeInt += pResampler->inAdvanceInt;
53220 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
53221 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
53222 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
53223 pResampler->inTimeInt += 1;
53224 }
53225 }
53226
53227 *pFrameCountIn = framesProcessedIn;
53228 *pFrameCountOut = framesProcessedOut;
53229
53230 return MA_SUCCESS;
53231}
53232
53233static 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)
53234{
53235 const ma_int16* pFramesInS16;
53236 /* */ ma_int16* pFramesOutS16;
53237 ma_uint64 frameCountIn;
53238 ma_uint64 frameCountOut;
53239 ma_uint64 framesProcessedIn;
53240 ma_uint64 framesProcessedOut;
53241
53242 MA_ASSERT(pResampler != NULL);
53243 MA_ASSERT(pFrameCountIn != NULL);
53244 MA_ASSERT(pFrameCountOut != NULL);
53245
53246 pFramesInS16 = (const ma_int16*)pFramesIn;
53247 pFramesOutS16 = ( ma_int16*)pFramesOut;
53248 frameCountIn = *pFrameCountIn;
53249 frameCountOut = *pFrameCountOut;
53250 framesProcessedIn = 0;
53251 framesProcessedOut = 0;
53252
53253 while (framesProcessedOut < frameCountOut) {
53254 /* Before interpolating we need to load the buffers. */
53255 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
53256 ma_uint32 iChannel;
53257
53258 if (pFramesInS16 != NULL) {
53259 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
53260 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
53261 pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
53262 }
53263 pFramesInS16 += pResampler->config.channels;
53264 } else {
53265 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
53266 pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
53267 pResampler->x1.s16[iChannel] = 0;
53268 }
53269 }
53270
53271 framesProcessedIn += 1;
53272 pResampler->inTimeInt -= 1;
53273 }
53274
53275 if (pResampler->inTimeInt > 0) {
53276 break; /* Ran out of input data. */
53277 }
53278
53279 /* Getting here means the frames have been loaded and we can generate the next output frame. */
53280 if (pFramesOutS16 != NULL) {
53281 MA_ASSERT(pResampler->inTimeInt == 0);
53282 ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
53283
53284 /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */
53285 if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) {
53286 ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16);
53287 }
53288
53289 pFramesOutS16 += pResampler->config.channels;
53290 }
53291
53292 framesProcessedOut += 1;
53293
53294 /* Advance time forward. */
53295 pResampler->inTimeInt += pResampler->inAdvanceInt;
53296 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
53297 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
53298 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
53299 pResampler->inTimeInt += 1;
53300 }
53301 }
53302
53303 *pFrameCountIn = framesProcessedIn;
53304 *pFrameCountOut = framesProcessedOut;
53305
53306 return MA_SUCCESS;
53307}
53308
53309static ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
53310{
53311 MA_ASSERT(pResampler != NULL);
53312
53313 if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
53314 return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
53315 } else {
53316 return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
53317 }
53318}
53319
53320
53321static 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)
53322{
53323 const float* pFramesInF32;
53324 /* */ float* pFramesOutF32;
53325 ma_uint64 frameCountIn;
53326 ma_uint64 frameCountOut;
53327 ma_uint64 framesProcessedIn;
53328 ma_uint64 framesProcessedOut;
53329
53330 MA_ASSERT(pResampler != NULL);
53331 MA_ASSERT(pFrameCountIn != NULL);
53332 MA_ASSERT(pFrameCountOut != NULL);
53333
53334 pFramesInF32 = (const float*)pFramesIn;
53335 pFramesOutF32 = ( float*)pFramesOut;
53336 frameCountIn = *pFrameCountIn;
53337 frameCountOut = *pFrameCountOut;
53338 framesProcessedIn = 0;
53339 framesProcessedOut = 0;
53340
53341 while (framesProcessedOut < frameCountOut) {
53342 /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */
53343 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
53344 ma_uint32 iChannel;
53345
53346 if (pFramesInF32 != NULL) {
53347 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
53348 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
53349 pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
53350 }
53351 pFramesInF32 += pResampler->config.channels;
53352 } else {
53353 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
53354 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
53355 pResampler->x1.f32[iChannel] = 0;
53356 }
53357 }
53358
53359 /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */
53360 if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) {
53361 ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32);
53362 }
53363
53364 framesProcessedIn += 1;
53365 pResampler->inTimeInt -= 1;
53366 }
53367
53368 if (pResampler->inTimeInt > 0) {
53369 break; /* Ran out of input data. */
53370 }
53371
53372 /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */
53373 if (pFramesOutF32 != NULL) {
53374 MA_ASSERT(pResampler->inTimeInt == 0);
53375 ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
53376
53377 pFramesOutF32 += pResampler->config.channels;
53378 }
53379
53380 framesProcessedOut += 1;
53381
53382 /* Advance time forward. */
53383 pResampler->inTimeInt += pResampler->inAdvanceInt;
53384 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
53385 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
53386 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
53387 pResampler->inTimeInt += 1;
53388 }
53389 }
53390
53391 *pFrameCountIn = framesProcessedIn;
53392 *pFrameCountOut = framesProcessedOut;
53393
53394 return MA_SUCCESS;
53395}
53396
53397static 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)
53398{
53399 const float* pFramesInF32;
53400 /* */ float* pFramesOutF32;
53401 ma_uint64 frameCountIn;
53402 ma_uint64 frameCountOut;
53403 ma_uint64 framesProcessedIn;
53404 ma_uint64 framesProcessedOut;
53405
53406 MA_ASSERT(pResampler != NULL);
53407 MA_ASSERT(pFrameCountIn != NULL);
53408 MA_ASSERT(pFrameCountOut != NULL);
53409
53410 pFramesInF32 = (const float*)pFramesIn;
53411 pFramesOutF32 = ( float*)pFramesOut;
53412 frameCountIn = *pFrameCountIn;
53413 frameCountOut = *pFrameCountOut;
53414 framesProcessedIn = 0;
53415 framesProcessedOut = 0;
53416
53417 while (framesProcessedOut < frameCountOut) {
53418 /* Before interpolating we need to load the buffers. */
53419 while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
53420 ma_uint32 iChannel;
53421
53422 if (pFramesInF32 != NULL) {
53423 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
53424 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
53425 pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
53426 }
53427 pFramesInF32 += pResampler->config.channels;
53428 } else {
53429 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
53430 pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
53431 pResampler->x1.f32[iChannel] = 0;
53432 }
53433 }
53434
53435 framesProcessedIn += 1;
53436 pResampler->inTimeInt -= 1;
53437 }
53438
53439 if (pResampler->inTimeInt > 0) {
53440 break; /* Ran out of input data. */
53441 }
53442
53443 /* Getting here means the frames have been loaded and we can generate the next output frame. */
53444 if (pFramesOutF32 != NULL) {
53445 MA_ASSERT(pResampler->inTimeInt == 0);
53446 ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
53447
53448 /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */
53449 if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) {
53450 ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32);
53451 }
53452
53453 pFramesOutF32 += pResampler->config.channels;
53454 }
53455
53456 framesProcessedOut += 1;
53457
53458 /* Advance time forward. */
53459 pResampler->inTimeInt += pResampler->inAdvanceInt;
53460 pResampler->inTimeFrac += pResampler->inAdvanceFrac;
53461 if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
53462 pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
53463 pResampler->inTimeInt += 1;
53464 }
53465 }
53466
53467 *pFrameCountIn = framesProcessedIn;
53468 *pFrameCountOut = framesProcessedOut;
53469
53470 return MA_SUCCESS;
53471}
53472
53473static ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
53474{
53475 MA_ASSERT(pResampler != NULL);
53476
53477 if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
53478 return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
53479 } else {
53480 return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
53481 }
53482}
53483
53484
53485MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
53486{
53487 if (pResampler == NULL) {
53488 return MA_INVALID_ARGS;
53489 }
53490
53491 /* */ if (pResampler->config.format == ma_format_s16) {
53492 return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
53493 } else if (pResampler->config.format == ma_format_f32) {
53494 return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
53495 } else {
53496 /* 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(). */
53497 MA_ASSERT(MA_FALSE);
53498 return MA_INVALID_ARGS;
53499 }
53500}
53501
53502
53503MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
53504{
53505 return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE);
53506}
53507
53508MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut)
53509{
53510 ma_uint32 n;
53511 ma_uint32 d;
53512
53513 if (pResampler == NULL) {
53514 return MA_INVALID_ARGS;
53515 }
53516
53517 if (ratioInOut <= 0) {
53518 return MA_INVALID_ARGS;
53519 }
53520
53521 d = 1000000;
53522 n = (ma_uint32)(ratioInOut * d);
53523
53524 if (n == 0) {
53525 return MA_INVALID_ARGS; /* Ratio too small. */
53526 }
53527
53528 MA_ASSERT(n != 0);
53529
53530 return ma_linear_resampler_set_rate(pResampler, n, d);
53531}
53532
53533MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler)
53534{
53535 if (pResampler == NULL) {
53536 return 0;
53537 }
53538
53539 return 1 + ma_lpf_get_latency(&pResampler->lpf);
53540}
53541
53542MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler)
53543{
53544 if (pResampler == NULL) {
53545 return 0;
53546 }
53547
53548 return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn;
53549}
53550
53551MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
53552{
53553 ma_uint64 inputFrameCount;
53554
53555 if (pInputFrameCount == NULL) {
53556 return MA_INVALID_ARGS;
53557 }
53558
53559 *pInputFrameCount = 0;
53560
53561 if (pResampler == NULL) {
53562 return MA_INVALID_ARGS;
53563 }
53564
53565 if (outputFrameCount == 0) {
53566 return MA_SUCCESS;
53567 }
53568
53569 /* Any whole input frames are consumed before the first output frame is generated. */
53570 inputFrameCount = pResampler->inTimeInt;
53571 outputFrameCount -= 1;
53572
53573 /* The rest of the output frames can be calculated in constant time. */
53574 inputFrameCount += outputFrameCount * pResampler->inAdvanceInt;
53575 inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut;
53576
53577 *pInputFrameCount = inputFrameCount;
53578
53579 return MA_SUCCESS;
53580}
53581
53582MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
53583{
53584 ma_uint64 outputFrameCount;
53585 ma_uint64 preliminaryInputFrameCountFromFrac;
53586 ma_uint64 preliminaryInputFrameCount;
53587
53588 if (pOutputFrameCount == NULL) {
53589 return MA_INVALID_ARGS;
53590 }
53591
53592 *pOutputFrameCount = 0;
53593
53594 if (pResampler == NULL) {
53595 return MA_INVALID_ARGS;
53596 }
53597
53598 /*
53599 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
53600 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
53601 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
53602 of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames.
53603 */
53604 outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn;
53605
53606 /*
53607 We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is
53608 used in the logic below to determine whether or not we need to add an extra output frame.
53609 */
53610 preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut;
53611 preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac;
53612
53613 /*
53614 If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greater than
53615 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
53616 to actually process. Otherwise we need to add the extra output frame.
53617 */
53618 if (preliminaryInputFrameCount <= inputFrameCount) {
53619 outputFrameCount += 1;
53620 }
53621
53622 *pOutputFrameCount = outputFrameCount;
53623
53624 return MA_SUCCESS;
53625}
53626
53627MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler)
53628{
53629 ma_uint32 iChannel;
53630
53631 if (pResampler == NULL) {
53632 return MA_INVALID_ARGS;
53633 }
53634
53635 /* Timers need to be cleared back to zero. */
53636 pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */
53637 pResampler->inTimeFrac = 0;
53638
53639 /* Cached samples need to be cleared. */
53640 if (pResampler->config.format == ma_format_f32) {
53641 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
53642 pResampler->x0.f32[iChannel] = 0;
53643 pResampler->x1.f32[iChannel] = 0;
53644 }
53645 } else {
53646 for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
53647 pResampler->x0.s16[iChannel] = 0;
53648 pResampler->x1.s16[iChannel] = 0;
53649 }
53650 }
53651
53652 /* The low pass filter needs to have its cache reset. */
53653 ma_lpf_clear_cache(&pResampler->lpf);
53654
53655 return MA_SUCCESS;
53656}
53657
53658
53659
53660/* Linear resampler backend vtable. */
53661static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig)
53662{
53663 ma_linear_resampler_config linearConfig;
53664
53665 linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut);
53666 linearConfig.lpfOrder = pConfig->linear.lpfOrder;
53667
53668 return linearConfig;
53669}
53670
53671static ma_result ma_resampling_backend_get_heap_size__linear(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes)
53672{
53673 ma_linear_resampler_config linearConfig;
53674
53675 (void)pUserData;
53676
53677 linearConfig = ma_resampling_backend_get_config__linear(pConfig);
53678
53679 return ma_linear_resampler_get_heap_size(&linearConfig, pHeapSizeInBytes);
53680}
53681
53682static ma_result ma_resampling_backend_init__linear(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend)
53683{
53684 ma_resampler* pResampler = (ma_resampler*)pUserData;
53685 ma_result result;
53686 ma_linear_resampler_config linearConfig;
53687
53688 (void)pUserData;
53689
53690 linearConfig = ma_resampling_backend_get_config__linear(pConfig);
53691
53692 result = ma_linear_resampler_init_preallocated(&linearConfig, pHeap, &pResampler->state.linear);
53693 if (result != MA_SUCCESS) {
53694 return result;
53695 }
53696
53697 *ppBackend = &pResampler->state.linear;
53698
53699 return MA_SUCCESS;
53700}
53701
53702static void ma_resampling_backend_uninit__linear(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
53703{
53704 (void)pUserData;
53705
53706 ma_linear_resampler_uninit((ma_linear_resampler*)pBackend, pAllocationCallbacks);
53707}
53708
53709static ma_result ma_resampling_backend_process__linear(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
53710{
53711 (void)pUserData;
53712
53713 return ma_linear_resampler_process_pcm_frames((ma_linear_resampler*)pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
53714}
53715
53716static ma_result ma_resampling_backend_set_rate__linear(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
53717{
53718 (void)pUserData;
53719
53720 return ma_linear_resampler_set_rate((ma_linear_resampler*)pBackend, sampleRateIn, sampleRateOut);
53721}
53722
53723static ma_uint64 ma_resampling_backend_get_input_latency__linear(void* pUserData, const ma_resampling_backend* pBackend)
53724{
53725 (void)pUserData;
53726
53727 return ma_linear_resampler_get_input_latency((const ma_linear_resampler*)pBackend);
53728}
53729
53730static ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserData, const ma_resampling_backend* pBackend)
53731{
53732 (void)pUserData;
53733
53734 return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend);
53735}
53736
53737static ma_result ma_resampling_backend_get_required_input_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
53738{
53739 (void)pUserData;
53740
53741 return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount);
53742}
53743
53744static ma_result ma_resampling_backend_get_expected_output_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
53745{
53746 (void)pUserData;
53747
53748 return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount);
53749}
53750
53751static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend)
53752{
53753 (void)pUserData;
53754
53755 return ma_linear_resampler_reset((ma_linear_resampler*)pBackend);
53756}
53757
53758static ma_resampling_backend_vtable g_ma_linear_resampler_vtable =
53759{
53760 ma_resampling_backend_get_heap_size__linear,
53761 ma_resampling_backend_init__linear,
53762 ma_resampling_backend_uninit__linear,
53763 ma_resampling_backend_process__linear,
53764 ma_resampling_backend_set_rate__linear,
53765 ma_resampling_backend_get_input_latency__linear,
53766 ma_resampling_backend_get_output_latency__linear,
53767 ma_resampling_backend_get_required_input_frame_count__linear,
53768 ma_resampling_backend_get_expected_output_frame_count__linear,
53769 ma_resampling_backend_reset__linear
53770};
53771
53772
53773
53775{
53776 ma_resampler_config config;
53777
53778 MA_ZERO_OBJECT(&config);
53779 config.format = format;
53780 config.channels = channels;
53781 config.sampleRateIn = sampleRateIn;
53782 config.sampleRateOut = sampleRateOut;
53783 config.algorithm = algorithm;
53784
53785 /* Linear. */
53786 config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
53787
53788 return config;
53789}
53790
53791static ma_result ma_resampler_get_vtable(const ma_resampler_config* pConfig, ma_resampler* pResampler, ma_resampling_backend_vtable** ppVTable, void** ppUserData)
53792{
53793 MA_ASSERT(pConfig != NULL);
53794 MA_ASSERT(ppVTable != NULL);
53795 MA_ASSERT(ppUserData != NULL);
53796
53797 /* Safety. */
53798 *ppVTable = NULL;
53799 *ppUserData = NULL;
53800
53801 switch (pConfig->algorithm)
53802 {
53804 {
53805 *ppVTable = &g_ma_linear_resampler_vtable;
53806 *ppUserData = pResampler;
53807 } break;
53808
53810 {
53811 *ppVTable = pConfig->pBackendVTable;
53812 *ppUserData = pConfig->pBackendUserData;
53813 } break;
53814
53815 default: return MA_INVALID_ARGS;
53816 }
53817
53818 return MA_SUCCESS;
53819}
53820
53821MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes)
53822{
53823 ma_result result;
53824 ma_resampling_backend_vtable* pVTable;
53825 void* pVTableUserData;
53826
53827 if (pHeapSizeInBytes == NULL) {
53828 return MA_INVALID_ARGS;
53829 }
53830
53831 *pHeapSizeInBytes = 0;
53832
53833 if (pConfig == NULL) {
53834 return MA_INVALID_ARGS;
53835 }
53836
53837 result = ma_resampler_get_vtable(pConfig, NULL, &pVTable, &pVTableUserData);
53838 if (result != MA_SUCCESS) {
53839 return result;
53840 }
53841
53842 if (pVTable == NULL || pVTable->onGetHeapSize == NULL) {
53843 return MA_NOT_IMPLEMENTED;
53844 }
53845
53846 result = pVTable->onGetHeapSize(pVTableUserData, pConfig, pHeapSizeInBytes);
53847 if (result != MA_SUCCESS) {
53848 return result;
53849 }
53850
53851 return MA_SUCCESS;
53852}
53853
53854MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler)
53855{
53856 ma_result result;
53857
53858 if (pResampler == NULL) {
53859 return MA_INVALID_ARGS;
53860 }
53861
53862 MA_ZERO_OBJECT(pResampler);
53863
53864 if (pConfig == NULL) {
53865 return MA_INVALID_ARGS;
53866 }
53867
53868 pResampler->_pHeap = pHeap;
53869 pResampler->format = pConfig->format;
53870 pResampler->channels = pConfig->channels;
53871 pResampler->sampleRateIn = pConfig->sampleRateIn;
53872 pResampler->sampleRateOut = pConfig->sampleRateOut;
53873
53874 result = ma_resampler_get_vtable(pConfig, pResampler, &pResampler->pBackendVTable, &pResampler->pBackendUserData);
53875 if (result != MA_SUCCESS) {
53876 return result;
53877 }
53878
53879 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onInit == NULL) {
53880 return MA_NOT_IMPLEMENTED; /* onInit not implemented. */
53881 }
53882
53883 result = pResampler->pBackendVTable->onInit(pResampler->pBackendUserData, pConfig, pHeap, &pResampler->pBackend);
53884 if (result != MA_SUCCESS) {
53885 return result;
53886 }
53887
53888 return MA_SUCCESS;
53889}
53890
53891MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler)
53892{
53893 ma_result result;
53894 size_t heapSizeInBytes;
53895 void* pHeap;
53896
53897 result = ma_resampler_get_heap_size(pConfig, &heapSizeInBytes);
53898 if (result != MA_SUCCESS) {
53899 return result;
53900 }
53901
53902 if (heapSizeInBytes > 0) {
53903 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
53904 if (pHeap == NULL) {
53905 return MA_OUT_OF_MEMORY;
53906 }
53907 } else {
53908 pHeap = NULL;
53909 }
53910
53911 result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler);
53912 if (result != MA_SUCCESS) {
53913 ma_free(pHeap, pAllocationCallbacks);
53914 return result;
53915 }
53916
53917 pResampler->_ownsHeap = MA_TRUE;
53918 return MA_SUCCESS;
53919}
53920
53921MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks)
53922{
53923 if (pResampler == NULL) {
53924 return;
53925 }
53926
53927 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onUninit == NULL) {
53928 return;
53929 }
53930
53931 pResampler->pBackendVTable->onUninit(pResampler->pBackendUserData, pResampler->pBackend, pAllocationCallbacks);
53932
53933 if (pResampler->_ownsHeap) {
53934 ma_free(pResampler->_pHeap, pAllocationCallbacks);
53935 }
53936}
53937
53938MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
53939{
53940 if (pResampler == NULL) {
53941 return MA_INVALID_ARGS;
53942 }
53943
53944 if (pFrameCountOut == NULL && pFrameCountIn == NULL) {
53945 return MA_INVALID_ARGS;
53946 }
53947
53948 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onProcess == NULL) {
53949 return MA_NOT_IMPLEMENTED;
53950 }
53951
53952 return pResampler->pBackendVTable->onProcess(pResampler->pBackendUserData, pResampler->pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
53953}
53954
53955MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
53956{
53957 ma_result result;
53958
53959 if (pResampler == NULL) {
53960 return MA_INVALID_ARGS;
53961 }
53962
53963 if (sampleRateIn == 0 || sampleRateOut == 0) {
53964 return MA_INVALID_ARGS;
53965 }
53966
53967 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onSetRate == NULL) {
53968 return MA_NOT_IMPLEMENTED;
53969 }
53970
53971 result = pResampler->pBackendVTable->onSetRate(pResampler->pBackendUserData, pResampler->pBackend, sampleRateIn, sampleRateOut);
53972 if (result != MA_SUCCESS) {
53973 return result;
53974 }
53975
53976 pResampler->sampleRateIn = sampleRateIn;
53977 pResampler->sampleRateOut = sampleRateOut;
53978
53979 return MA_SUCCESS;
53980}
53981
53982MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio)
53983{
53984 ma_uint32 n;
53985 ma_uint32 d;
53986
53987 if (pResampler == NULL) {
53988 return MA_INVALID_ARGS;
53989 }
53990
53991 if (ratio <= 0) {
53992 return MA_INVALID_ARGS;
53993 }
53994
53995 d = 1000;
53996 n = (ma_uint32)(ratio * d);
53997
53998 if (n == 0) {
53999 return MA_INVALID_ARGS; /* Ratio too small. */
54000 }
54001
54002 MA_ASSERT(n != 0);
54003
54004 return ma_resampler_set_rate(pResampler, n, d);
54005}
54006
54007MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler)
54008{
54009 if (pResampler == NULL) {
54010 return 0;
54011 }
54012
54013 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetInputLatency == NULL) {
54014 return 0;
54015 }
54016
54017 return pResampler->pBackendVTable->onGetInputLatency(pResampler->pBackendUserData, pResampler->pBackend);
54018}
54019
54020MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler)
54021{
54022 if (pResampler == NULL) {
54023 return 0;
54024 }
54025
54026 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetOutputLatency == NULL) {
54027 return 0;
54028 }
54029
54030 return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend);
54031}
54032
54033MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
54034{
54035 if (pInputFrameCount == NULL) {
54036 return MA_INVALID_ARGS;
54037 }
54038
54039 *pInputFrameCount = 0;
54040
54041 if (pResampler == NULL) {
54042 return MA_INVALID_ARGS;
54043 }
54044
54045 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) {
54046 return MA_NOT_IMPLEMENTED;
54047 }
54048
54049 return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount);
54050}
54051
54052MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
54053{
54054 if (pOutputFrameCount == NULL) {
54055 return MA_INVALID_ARGS;
54056 }
54057
54058 *pOutputFrameCount = 0;
54059
54060 if (pResampler == NULL) {
54061 return MA_INVALID_ARGS;
54062 }
54063
54064 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) {
54065 return MA_NOT_IMPLEMENTED;
54066 }
54067
54068 return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount);
54069}
54070
54071MA_API ma_result ma_resampler_reset(ma_resampler* pResampler)
54072{
54073 if (pResampler == NULL) {
54074 return MA_INVALID_ARGS;
54075 }
54076
54077 if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onReset == NULL) {
54078 return MA_NOT_IMPLEMENTED;
54079 }
54080
54081 return pResampler->pBackendVTable->onReset(pResampler->pBackendUserData, pResampler->pBackend);
54082}
54083
54084/**************************************************************************************************************************************************************
54085
54086Channel Conversion
54087
54088**************************************************************************************************************************************************************/
54089#ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT
54090#define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12
54091#endif
54092
54093#define MA_PLANE_LEFT 0
54094#define MA_PLANE_RIGHT 1
54095#define MA_PLANE_FRONT 2
54096#define MA_PLANE_BACK 3
54097#define MA_PLANE_BOTTOM 4
54098#define MA_PLANE_TOP 5
54099
54100static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = {
54101 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */
54102 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */
54103 { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */
54104 { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */
54105 { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */
54106 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */
54107 { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */
54108 { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */
54109 { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */
54110 { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */
54111 { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */
54112 { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */
54113 { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */
54114 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */
54115 { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */
54116 { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */
54117 { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */
54118 { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */
54119 { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */
54120 { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */
54121 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */
54122 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */
54123 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */
54124 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */
54125 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */
54126 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */
54127 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */
54128 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */
54129 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */
54130 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */
54131 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */
54132 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */
54133 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */
54134 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */
54135 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */
54136 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */
54137 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */
54138 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */
54139 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */
54140 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */
54141 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */
54142 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */
54143 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */
54144 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */
54145 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */
54146 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */
54147 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */
54148 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */
54149 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */
54150 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */
54151 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */
54152 { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */
54153};
54154
54155static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB)
54156{
54157 /*
54158 Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to
54159 the following output configuration:
54160
54161 - front/left
54162 - side/left
54163 - back/left
54164
54165 The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount
54166 of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated.
54167
54168 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
54169 speaker emitting half of its total volume from the front, and the other half from the left. Since part of its volume is being emitted
54170 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
54171 receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between
54172 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
54173 across 3 spatial dimensions.
54174
54175 The first thing to do is figure out how each speaker's volume is spread over each of plane:
54176 - front/left: 2 planes (front and left) = 1/2 = half its total volume on each plane
54177 - side/left: 1 plane (left only) = 1/1 = entire volume from left plane
54178 - back/left: 2 planes (back and left) = 1/2 = half its total volume on each plane
54179 - top/front/left: 3 planes (top, front and left) = 1/3 = one third its total volume on each plane
54180
54181 The amount of volume each channel contributes to each of its planes is what controls how much it is willing to given and take to other
54182 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
54183 taken by the other to produce the final contribution.
54184 */
54185
54186 /* Contribution = Sum(Volume to Give * Volume to Take) */
54187 float contribution =
54188 g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] +
54189 g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] +
54190 g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] +
54191 g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] +
54192 g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] +
54193 g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5];
54194
54195 return contribution;
54196}
54197
54198MA_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)
54199{
54200 ma_channel_converter_config config;
54201
54202 MA_ZERO_OBJECT(&config);
54203 config.format = format;
54204 config.channelsIn = channelsIn;
54205 config.channelsOut = channelsOut;
54206 config.pChannelMapIn = pChannelMapIn;
54207 config.pChannelMapOut = pChannelMapOut;
54208 config.mixingMode = mixingMode;
54209
54210 return config;
54211}
54212
54213static ma_int32 ma_channel_converter_float_to_fixed(float x)
54214{
54215 return (ma_int32)(x * (1<<MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT));
54216}
54217
54218static ma_uint32 ma_channel_map_get_spatial_channel_count(const ma_channel* pChannelMap, ma_uint32 channels)
54219{
54220 ma_uint32 spatialChannelCount = 0;
54221 ma_uint32 iChannel;
54222
54223 MA_ASSERT(pChannelMap != NULL);
54224 MA_ASSERT(channels > 0);
54225
54226 for (iChannel = 0; iChannel < channels; ++iChannel) {
54227 if (ma_is_spatial_channel_position(ma_channel_map_get_channel(pChannelMap, channels, iChannel))) {
54228 spatialChannelCount++;
54229 }
54230 }
54231
54232 return spatialChannelCount;
54233}
54234
54235static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition)
54236{
54237 int i;
54238
54239 if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) {
54240 return MA_FALSE;
54241 }
54242
54243 if (channelPosition >= MA_CHANNEL_AUX_0 && channelPosition <= MA_CHANNEL_AUX_31) {
54244 return MA_FALSE;
54245 }
54246
54247 for (i = 0; i < 6; ++i) { /* Each side of a cube. */
54248 if (g_maChannelPlaneRatios[channelPosition][i] != 0) {
54249 return MA_TRUE;
54250 }
54251 }
54252
54253 return MA_FALSE;
54254}
54255
54256
54257static ma_bool32 ma_channel_map_is_passthrough(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut)
54258{
54259 if (channelsOut == channelsIn) {
54260 return ma_channel_map_is_equal(pChannelMapOut, pChannelMapIn, channelsOut);
54261 } else {
54262 return MA_FALSE; /* Channel counts differ, so cannot be a passthrough. */
54263 }
54264}
54265
54266static 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)
54267{
54268 if (ma_channel_map_is_passthrough(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut)) {
54270 }
54271
54272 if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) {
54274 }
54275
54276 if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) {
54278 }
54279
54282 }
54283
54284 /*
54285 We can use a simple shuffle if both channel maps have the same channel count and all channel
54286 positions are present in both.
54287 */
54288 if (channelsIn == channelsOut) {
54289 ma_uint32 iChannelIn;
54290 ma_bool32 areAllChannelPositionsPresent = MA_TRUE;
54291 for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) {
54292 ma_bool32 isInputChannelPositionInOutput = ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn));
54293 if (!isInputChannelPositionInOutput) {
54294 areAllChannelPositionsPresent = MA_FALSE;
54295 break;
54296 }
54297 }
54298
54299 if (areAllChannelPositionsPresent) {
54301 }
54302 }
54303
54304 /* Getting here means we'll need to use weights. */
54306}
54307
54308
54309static 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)
54310{
54311 ma_uint32 iChannelIn;
54312 ma_uint32 iChannelOut;
54313
54314 if (pShuffleTable == NULL || channelCountIn == 0 || channelCountOut == 0) {
54315 return MA_INVALID_ARGS;
54316 }
54317
54318 /*
54319 When building the shuffle table we just do a 1:1 mapping based on the first occurrence of a channel. If the
54320 input channel has more than one occurrence of a channel position, the second one will be ignored.
54321 */
54322 for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) {
54323 ma_channel channelOut;
54324
54325 /* Default to MA_CHANNEL_INDEX_NULL so that if a mapping is not found it'll be set appropriately. */
54326 pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL;
54327
54328 channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut);
54329 for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) {
54330 ma_channel channelIn;
54331
54332 channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn);
54333 if (channelOut == channelIn) {
54334 pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
54335 break;
54336 }
54337
54338 /*
54339 Getting here means the channels don't exactly match, but we are going to support some
54340 relaxed matching for practicality. If, for example, there are two stereo channel maps,
54341 but one uses front left/right and the other uses side left/right, it makes logical
54342 sense to just map these. The way we'll do it is we'll check if there is a logical
54343 corresponding mapping, and if so, apply it, but we will *not* break from the loop,
54344 thereby giving the loop a chance to find an exact match later which will take priority.
54345 */
54346 switch (channelOut)
54347 {
54348 /* Left channels. */
54351 {
54352 switch (channelIn) {
54355 {
54356 pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
54357 } break;
54358 }
54359 } break;
54360
54361 /* Right channels. */
54364 {
54365 switch (channelIn) {
54368 {
54369 pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
54370 } break;
54371 }
54372 } break;
54373
54374 default: break;
54375 }
54376 }
54377 }
54378
54379 return MA_SUCCESS;
54380}
54381
54382
54383static 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)
54384{
54385 ma_uint64 iFrame;
54386 ma_uint32 iChannelOut;
54387
54388 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
54389 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54390 ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
54391 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
54392 pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
54393 } else {
54394 pFramesOut[iChannelOut] = 0;
54395 }
54396 }
54397
54398 pFramesOut += channelsOut;
54399 pFramesIn += channelsIn;
54400 }
54401}
54402
54403static 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)
54404{
54405 ma_uint64 iFrame;
54406 ma_uint32 iChannelOut;
54407
54408 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
54409 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54410 ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
54411 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
54412 pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
54413 } else {
54414 pFramesOut[iChannelOut] = 0;
54415 }
54416 }
54417
54418 pFramesOut += channelsOut;
54419 pFramesIn += channelsIn;
54420 }
54421}
54422
54423static 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)
54424{
54425 ma_uint64 iFrame;
54426 ma_uint32 iChannelOut;
54427
54428 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
54429 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54430 ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
54431 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
54432 pFramesOut[iChannelOut*3 + 0] = pFramesIn[iChannelIn*3 + 0];
54433 pFramesOut[iChannelOut*3 + 1] = pFramesIn[iChannelIn*3 + 1];
54434 pFramesOut[iChannelOut*3 + 2] = pFramesIn[iChannelIn*3 + 2];
54435 } else {
54436 pFramesOut[iChannelOut*3 + 0] = 0;
54437 } pFramesOut[iChannelOut*3 + 1] = 0;
54438 } pFramesOut[iChannelOut*3 + 2] = 0;
54439
54440 pFramesOut += channelsOut*3;
54441 pFramesIn += channelsIn*3;
54442 }
54443}
54444
54445static 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)
54446{
54447 ma_uint64 iFrame;
54448 ma_uint32 iChannelOut;
54449
54450 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
54451 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54452 ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
54453 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
54454 pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
54455 } else {
54456 pFramesOut[iChannelOut] = 0;
54457 }
54458 }
54459
54460 pFramesOut += channelsOut;
54461 pFramesIn += channelsIn;
54462 }
54463}
54464
54465static 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)
54466{
54467 ma_uint64 iFrame;
54468 ma_uint32 iChannelOut;
54469
54470 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
54471 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54472 ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
54473 if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */
54474 pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
54475 } else {
54476 pFramesOut[iChannelOut] = 0;
54477 }
54478 }
54479
54480 pFramesOut += channelsOut;
54481 pFramesIn += channelsIn;
54482 }
54483}
54484
54485static 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)
54486{
54487 if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || pShuffleTable == NULL) {
54488 return MA_INVALID_ARGS;
54489 }
54490
54491 switch (format)
54492 {
54493 case ma_format_u8:
54494 {
54495 ma_channel_map_apply_shuffle_table_u8((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable);
54496 } break;
54497
54498 case ma_format_s16:
54499 {
54500 ma_channel_map_apply_shuffle_table_s16((ma_int16*)pFramesOut, channelsOut, (const ma_int16*)pFramesIn, channelsIn, frameCount, pShuffleTable);
54501 } break;
54502
54503 case ma_format_s24:
54504 {
54505 ma_channel_map_apply_shuffle_table_s24((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable);
54506 } break;
54507
54508 case ma_format_s32:
54509 {
54510 ma_channel_map_apply_shuffle_table_s32((ma_int32*)pFramesOut, channelsOut, (const ma_int32*)pFramesIn, channelsIn, frameCount, pShuffleTable);
54511 } break;
54512
54513 case ma_format_f32:
54514 {
54515 ma_channel_map_apply_shuffle_table_f32((float*)pFramesOut, channelsOut, (const float*)pFramesIn, channelsIn, frameCount, pShuffleTable);
54516 } break;
54517
54518 default: return MA_INVALID_ARGS; /* Unknown format. */
54519 }
54520
54521 return MA_SUCCESS;
54522}
54523
54524static ma_result ma_channel_map_apply_mono_out_f32(float* pFramesOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount)
54525{
54526 ma_uint64 iFrame;
54527 ma_uint32 iChannelIn;
54528 ma_uint32 accumulationCount;
54529
54530 if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) {
54531 return MA_INVALID_ARGS;
54532 }
54533
54534 /* In this case the output stream needs to be the average of all channels, ignoring NONE. */
54535
54536 /* A quick pre-processing step to get the accumulation counter since we're ignoring NONE channels. */
54537 accumulationCount = 0;
54538 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
54539 if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) {
54540 accumulationCount += 1;
54541 }
54542 }
54543
54544 if (accumulationCount > 0) { /* <-- Prevent a division by zero. */
54545 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
54546 float accumulation = 0;
54547
54548 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
54549 ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
54550 if (channelIn != MA_CHANNEL_NONE) {
54551 accumulation += pFramesIn[iChannelIn];
54552 }
54553 }
54554
54555 pFramesOut[0] = accumulation / accumulationCount;
54556 pFramesOut += 1;
54557 pFramesIn += channelsIn;
54558 }
54559 } else {
54560 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1);
54561 }
54562
54563 return MA_SUCCESS;
54564}
54565
54566static 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)
54567{
54568 ma_uint64 iFrame;
54569 ma_uint32 iChannelOut;
54570
54571 if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) {
54572 return MA_INVALID_ARGS;
54573 }
54574
54575 /* Note that the MA_CHANNEL_NONE channel must be ignored in all cases. */
54576 switch (monoExpansionMode)
54577 {
54579 {
54580 float weight;
54581 ma_uint32 validChannelCount = 0;
54582
54583 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54584 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
54585 if (channelOut != MA_CHANNEL_NONE) {
54586 validChannelCount += 1;
54587 }
54588 }
54589
54590 weight = 1.0f / validChannelCount;
54591
54592 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
54593 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54594 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
54595 if (channelOut != MA_CHANNEL_NONE) {
54596 pFramesOut[iChannelOut] = pFramesIn[0] * weight;
54597 }
54598 }
54599
54600 pFramesOut += channelsOut;
54601 pFramesIn += 1;
54602 }
54603 } break;
54604
54606 {
54607 if (channelsOut >= 2) {
54608 ma_uint32 iChannelLeft = (ma_uint32)-1;
54609 ma_uint32 iChannelRight = (ma_uint32)-1;
54610
54611 /*
54612 We first need to find our stereo channels. We prefer front-left and front-right, but
54613 if they're not available, we'll also try side-left and side-right. If neither are
54614 available we'll fall through to the default case below.
54615 */
54616 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54617 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
54618 if (channelOut == MA_CHANNEL_SIDE_LEFT) {
54619 iChannelLeft = iChannelOut;
54620 }
54621 if (channelOut == MA_CHANNEL_SIDE_RIGHT) {
54622 iChannelRight = iChannelOut;
54623 }
54624 }
54625
54626 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54627 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
54628 if (channelOut == MA_CHANNEL_FRONT_LEFT) {
54629 iChannelLeft = iChannelOut;
54630 }
54631 if (channelOut == MA_CHANNEL_FRONT_RIGHT) {
54632 iChannelRight = iChannelOut;
54633 }
54634 }
54635
54636
54637 if (iChannelLeft != (ma_uint32)-1 && iChannelRight != (ma_uint32)-1) {
54638 /* We found our stereo channels so we can duplicate the signal across those channels. */
54639 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
54640 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54641 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
54642 if (channelOut != MA_CHANNEL_NONE) {
54643 if (iChannelOut == iChannelLeft || iChannelOut == iChannelRight) {
54644 pFramesOut[iChannelOut] = pFramesIn[0];
54645 } else {
54646 pFramesOut[iChannelOut] = 0.0f;
54647 }
54648 }
54649 }
54650
54651 pFramesOut += channelsOut;
54652 pFramesIn += 1;
54653 }
54654
54655 break; /* Get out of the switch. */
54656 } else {
54657 /* Fallthrough. Does not have left and right channels. */
54658 goto default_handler;
54659 }
54660 } else {
54661 /* Fallthrough. Does not have stereo channels. */
54662 goto default_handler;
54663 }
54664 }; /* Fallthrough. See comments above. */
54665
54667 default:
54668 {
54669 default_handler:
54670 {
54671 if (channelsOut <= MA_MAX_CHANNELS) {
54672 ma_bool32 hasEmptyChannel = MA_FALSE;
54673 ma_channel channelPositions[MA_MAX_CHANNELS];
54674 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54675 channelPositions[iChannelOut] = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
54676 if (channelPositions[iChannelOut] == MA_CHANNEL_NONE) {
54677 hasEmptyChannel = MA_TRUE;
54678 }
54679 }
54680
54681 if (hasEmptyChannel == MA_FALSE) {
54682 /*
54683 Faster path when there's no MA_CHANNEL_NONE channel positions. This should hopefully
54684 help the compiler with auto-vectorization.m
54685 */
54686 if (channelsOut == 2) {
54687 #if defined(MA_SUPPORT_SSE2)
54688 if (ma_has_sse2()) {
54689 /* We want to do two frames in each iteration. */
54690 ma_uint64 unrolledFrameCount = frameCount >> 1;
54691
54692 for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) {
54693 __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]);
54694 __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]);
54695 _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0)));
54696 }
54697
54698 /* Tail. */
54699 iFrame = unrolledFrameCount << 1;
54700 goto generic_on_fastpath;
54701 } else
54702 #endif
54703 {
54704 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
54705 for (iChannelOut = 0; iChannelOut < 2; iChannelOut += 1) {
54706 pFramesOut[iFrame*2 + iChannelOut] = pFramesIn[iFrame];
54707 }
54708 }
54709 }
54710 } else if (channelsOut == 6) {
54711 #if defined(MA_SUPPORT_SSE2)
54712 if (ma_has_sse2()) {
54713 /* We want to do two frames in each iteration so we can have a multiple of 4 samples. */
54714 ma_uint64 unrolledFrameCount = frameCount >> 1;
54715
54716 for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) {
54717 __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]);
54718 __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]);
54719
54720 _mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0);
54721 _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0)));
54722 _mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1);
54723 }
54724
54725 /* Tail. */
54726 iFrame = unrolledFrameCount << 1;
54727 goto generic_on_fastpath;
54728 } else
54729 #endif
54730 {
54731 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
54732 for (iChannelOut = 0; iChannelOut < 6; iChannelOut += 1) {
54733 pFramesOut[iFrame*6 + iChannelOut] = pFramesIn[iFrame];
54734 }
54735 }
54736 }
54737 } else if (channelsOut == 8) {
54738 #if defined(MA_SUPPORT_SSE2)
54739 if (ma_has_sse2()) {
54740 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
54741 __m128 in = _mm_set1_ps(pFramesIn[iFrame]);
54742 _mm_storeu_ps(&pFramesOut[iFrame*8 + 0], in);
54743 _mm_storeu_ps(&pFramesOut[iFrame*8 + 4], in);
54744 }
54745 } else
54746 #endif
54747 {
54748 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
54749 for (iChannelOut = 0; iChannelOut < 8; iChannelOut += 1) {
54750 pFramesOut[iFrame*8 + iChannelOut] = pFramesIn[iFrame];
54751 }
54752 }
54753 }
54754 } else {
54755 iFrame = 0;
54756
54757 #if defined(MA_SUPPORT_SSE2) /* For silencing a warning with non-x86 builds. */
54758 generic_on_fastpath:
54759 #endif
54760 {
54761 for (; iFrame < frameCount; iFrame += 1) {
54762 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54763 pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame];
54764 }
54765 }
54766 }
54767 }
54768 } else {
54769 /* Slow path. Need to handle MA_CHANNEL_NONE. */
54770 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
54771 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54772 if (channelPositions[iChannelOut] != MA_CHANNEL_NONE) {
54773 pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame];
54774 }
54775 }
54776 }
54777 }
54778 } else {
54779 /* Slow path. Too many channels to store on the stack. */
54780 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
54781 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54782 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
54783 if (channelOut != MA_CHANNEL_NONE) {
54784 pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame];
54785 }
54786 }
54787 }
54788 }
54789 }
54790 } break;
54791 }
54792
54793 return MA_SUCCESS;
54794}
54795
54796static 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)
54797{
54798 ma_channel_conversion_path conversionPath = ma_channel_map_get_conversion_path(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, mode);
54799
54800 /* Optimized Path: Passthrough */
54801 if (conversionPath == ma_channel_conversion_path_passthrough) {
54802 ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut);
54803 return;
54804 }
54805
54806 /* Special Path: Mono Output. */
54807 if (conversionPath == ma_channel_conversion_path_mono_out) {
54808 ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount);
54809 return;
54810 }
54811
54812 /* Special Path: Mono Input. */
54813 if (conversionPath == ma_channel_conversion_path_mono_in) {
54814 ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount, monoExpansionMode);
54815 return;
54816 }
54817
54818 /* Getting here means we aren't running on an optimized conversion path. */
54819 if (channelsOut <= MA_MAX_CHANNELS) {
54820 ma_result result;
54821
54822 if (mode == ma_channel_mix_mode_simple) {
54823 ma_channel shuffleTable[MA_MAX_CHANNELS];
54824
54825 result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable);
54826 if (result != MA_SUCCESS) {
54827 return;
54828 }
54829
54830 result = ma_channel_map_apply_shuffle_table(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable, ma_format_f32);
54831 if (result != MA_SUCCESS) {
54832 return;
54833 }
54834 } else {
54835 ma_uint32 iFrame;
54836 ma_uint32 iChannelOut;
54837 ma_uint32 iChannelIn;
54838 float weights[32][32]; /* Do not use MA_MAX_CHANNELS here! */
54839
54840 /*
54841 If we have a small enough number of channels, pre-compute the weights. Otherwise we'll just need to
54842 fall back to a slower path because otherwise we'll run out of stack space.
54843 */
54844 if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) {
54845 /* Pre-compute weights. */
54846 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54847 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
54848 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
54849 ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
54850 weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn);
54851 }
54852 }
54853
54854 iFrame = 0;
54855
54856 /* Experiment: Try an optimized unroll for some specific cases to see how it improves performance. RESULT: Good gains. */
54857 if (channelsOut == 8) {
54858 /* Experiment 2: Expand the inner loop to see what kind of different it makes. RESULT: Small, but worthwhile gain. */
54859 if (channelsIn == 2) {
54860 for (; iFrame < frameCount; iFrame += 1) {
54861 float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
54862
54863 accumulation[0] += pFramesIn[iFrame*2 + 0] * weights[0][0];
54864 accumulation[1] += pFramesIn[iFrame*2 + 0] * weights[1][0];
54865 accumulation[2] += pFramesIn[iFrame*2 + 0] * weights[2][0];
54866 accumulation[3] += pFramesIn[iFrame*2 + 0] * weights[3][0];
54867 accumulation[4] += pFramesIn[iFrame*2 + 0] * weights[4][0];
54868 accumulation[5] += pFramesIn[iFrame*2 + 0] * weights[5][0];
54869 accumulation[6] += pFramesIn[iFrame*2 + 0] * weights[6][0];
54870 accumulation[7] += pFramesIn[iFrame*2 + 0] * weights[7][0];
54871
54872 accumulation[0] += pFramesIn[iFrame*2 + 1] * weights[0][1];
54873 accumulation[1] += pFramesIn[iFrame*2 + 1] * weights[1][1];
54874 accumulation[2] += pFramesIn[iFrame*2 + 1] * weights[2][1];
54875 accumulation[3] += pFramesIn[iFrame*2 + 1] * weights[3][1];
54876 accumulation[4] += pFramesIn[iFrame*2 + 1] * weights[4][1];
54877 accumulation[5] += pFramesIn[iFrame*2 + 1] * weights[5][1];
54878 accumulation[6] += pFramesIn[iFrame*2 + 1] * weights[6][1];
54879 accumulation[7] += pFramesIn[iFrame*2 + 1] * weights[7][1];
54880
54881 pFramesOut[iFrame*8 + 0] = accumulation[0];
54882 pFramesOut[iFrame*8 + 1] = accumulation[1];
54883 pFramesOut[iFrame*8 + 2] = accumulation[2];
54884 pFramesOut[iFrame*8 + 3] = accumulation[3];
54885 pFramesOut[iFrame*8 + 4] = accumulation[4];
54886 pFramesOut[iFrame*8 + 5] = accumulation[5];
54887 pFramesOut[iFrame*8 + 6] = accumulation[6];
54888 pFramesOut[iFrame*8 + 7] = accumulation[7];
54889 }
54890 } else {
54891 /* When outputting to 8 channels, we can do everything in groups of two 4x SIMD operations. */
54892 for (; iFrame < frameCount; iFrame += 1) {
54893 float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
54894
54895 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
54896 accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn];
54897 accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn];
54898 accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn];
54899 accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn];
54900 accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn];
54901 accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn];
54902 accumulation[6] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[6][iChannelIn];
54903 accumulation[7] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[7][iChannelIn];
54904 }
54905
54906 pFramesOut[iFrame*8 + 0] = accumulation[0];
54907 pFramesOut[iFrame*8 + 1] = accumulation[1];
54908 pFramesOut[iFrame*8 + 2] = accumulation[2];
54909 pFramesOut[iFrame*8 + 3] = accumulation[3];
54910 pFramesOut[iFrame*8 + 4] = accumulation[4];
54911 pFramesOut[iFrame*8 + 5] = accumulation[5];
54912 pFramesOut[iFrame*8 + 6] = accumulation[6];
54913 pFramesOut[iFrame*8 + 7] = accumulation[7];
54914 }
54915 }
54916 } else if (channelsOut == 6) {
54917 /*
54918 When outputting to 6 channels we unfortunately don't have a nice multiple of 4 to do 4x SIMD operations. Instead we'll
54919 expand our weights and do two frames at a time.
54920 */
54921 for (; iFrame < frameCount; iFrame += 1) {
54922 float accumulation[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
54923
54924 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
54925 accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn];
54926 accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn];
54927 accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn];
54928 accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn];
54929 accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn];
54930 accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn];
54931 }
54932
54933 pFramesOut[iFrame*6 + 0] = accumulation[0];
54934 pFramesOut[iFrame*6 + 1] = accumulation[1];
54935 pFramesOut[iFrame*6 + 2] = accumulation[2];
54936 pFramesOut[iFrame*6 + 3] = accumulation[3];
54937 pFramesOut[iFrame*6 + 4] = accumulation[4];
54938 pFramesOut[iFrame*6 + 5] = accumulation[5];
54939 }
54940 }
54941
54942 /* Leftover frames. */
54943 for (; iFrame < frameCount; iFrame += 1) {
54944 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54945 float accumulation = 0;
54946
54947 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
54948 accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[iChannelOut][iChannelIn];
54949 }
54950
54951 pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation;
54952 }
54953 }
54954 } else {
54955 /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */
54956 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
54957 for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
54958 float accumulation = 0;
54959 ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
54960
54961 for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
54962 ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
54963 accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn);
54964 }
54965
54966 pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation;
54967 }
54968 }
54969 }
54970 }
54971 } else {
54972 /* Fall back to silence. If you hit this, what are you doing with so many channels?! */
54973 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut);
54974 }
54975}
54976
54977
54978typedef struct
54979{
54980 size_t sizeInBytes;
54981 size_t channelMapInOffset;
54982 size_t channelMapOutOffset;
54983 size_t shuffleTableOffset;
54984 size_t weightsOffset;
54985} ma_channel_converter_heap_layout;
54986
54987static ma_channel_conversion_path ma_channel_converter_config_get_conversion_path(const ma_channel_converter_config* pConfig)
54988{
54989 return ma_channel_map_get_conversion_path(pConfig->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapOut, pConfig->channelsOut, pConfig->mixingMode);
54990}
54991
54992static ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter_config* pConfig, ma_channel_converter_heap_layout* pHeapLayout)
54993{
54994 ma_channel_conversion_path conversionPath;
54995
54996 MA_ASSERT(pHeapLayout != NULL);
54997
54998 if (pConfig == NULL) {
54999 return MA_INVALID_ARGS;
55000 }
55001
55002 if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
55003 return MA_INVALID_ARGS;
55004 }
55005
55006 if (!ma_channel_map_is_valid(pConfig->pChannelMapIn, pConfig->channelsIn)) {
55007 return MA_INVALID_ARGS;
55008 }
55009
55010 if (!ma_channel_map_is_valid(pConfig->pChannelMapOut, pConfig->channelsOut)) {
55011 return MA_INVALID_ARGS;
55012 }
55013
55014 pHeapLayout->sizeInBytes = 0;
55015
55016 /* Input channel map. Only need to allocate this if we have an input channel map (otherwise default channel map is assumed). */
55017 pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes;
55018 if (pConfig->pChannelMapIn != NULL) {
55019 pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn;
55020 }
55021
55022 /* Output channel map. Only need to allocate this if we have an output channel map (otherwise default channel map is assumed). */
55023 pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes;
55024 if (pConfig->pChannelMapOut != NULL) {
55025 pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut;
55026 }
55027
55028 /* Alignment for the next section. */
55029 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
55030
55031 /* Whether or not we use weights of a shuffle table depends on the channel map themselves and the algorithm we've chosen. */
55032 conversionPath = ma_channel_converter_config_get_conversion_path(pConfig);
55033
55034 /* Shuffle table */
55035 pHeapLayout->shuffleTableOffset = pHeapLayout->sizeInBytes;
55036 if (conversionPath == ma_channel_conversion_path_shuffle) {
55037 pHeapLayout->sizeInBytes += sizeof(ma_uint8) * pConfig->channelsOut;
55038 }
55039
55040 /* Weights */
55041 pHeapLayout->weightsOffset = pHeapLayout->sizeInBytes;
55042 if (conversionPath == ma_channel_conversion_path_weights) {
55043 pHeapLayout->sizeInBytes += sizeof(float*) * pConfig->channelsIn;
55044 pHeapLayout->sizeInBytes += sizeof(float ) * pConfig->channelsIn * pConfig->channelsOut;
55045 }
55046
55047 /* Make sure allocation size is aligned. */
55048 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
55049
55050 return MA_SUCCESS;
55051}
55052
55053MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes)
55054{
55055 ma_result result;
55056 ma_channel_converter_heap_layout heapLayout;
55057
55058 if (pHeapSizeInBytes == NULL) {
55059 return MA_INVALID_ARGS;
55060 }
55061
55062 *pHeapSizeInBytes = 0;
55063
55064 result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout);
55065 if (result != MA_SUCCESS) {
55066 return result;
55067 }
55068
55069 *pHeapSizeInBytes = heapLayout.sizeInBytes;
55070
55071 return MA_SUCCESS;
55072}
55073
55074MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter)
55075{
55076 ma_result result;
55077 ma_channel_converter_heap_layout heapLayout;
55078
55079 if (pConverter == NULL) {
55080 return MA_INVALID_ARGS;
55081 }
55082
55083 MA_ZERO_OBJECT(pConverter);
55084
55085 result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout);
55086 if (result != MA_SUCCESS) {
55087 return result;
55088 }
55089
55090 pConverter->_pHeap = pHeap;
55091 MA_ZERO_MEMORY(pConverter->_pHeap, heapLayout.sizeInBytes);
55092
55093 pConverter->format = pConfig->format;
55094 pConverter->channelsIn = pConfig->channelsIn;
55095 pConverter->channelsOut = pConfig->channelsOut;
55096 pConverter->mixingMode = pConfig->mixingMode;
55097
55098 if (pConfig->pChannelMapIn != NULL) {
55099 pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset);
55100 ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn);
55101 } else {
55102 pConverter->pChannelMapIn = NULL; /* Use default channel map. */
55103 }
55104
55105 if (pConfig->pChannelMapOut != NULL) {
55106 pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset);
55107 ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut);
55108 } else {
55109 pConverter->pChannelMapOut = NULL; /* Use default channel map. */
55110 }
55111
55112 pConverter->conversionPath = ma_channel_converter_config_get_conversion_path(pConfig);
55113
55115 pConverter->pShuffleTable = (ma_uint8*)ma_offset_ptr(pHeap, heapLayout.shuffleTableOffset);
55116 ma_channel_map_build_shuffle_table(pConverter->pChannelMapIn, pConverter->channelsIn, pConverter->pChannelMapOut, pConverter->channelsOut, pConverter->pShuffleTable);
55117 }
55118
55120 ma_uint32 iChannelIn;
55121 ma_uint32 iChannelOut;
55122
55123 if (pConverter->format == ma_format_f32) {
55124 pConverter->weights.f32 = (float** )ma_offset_ptr(pHeap, heapLayout.weightsOffset);
55125 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
55126 pConverter->weights.f32[iChannelIn] = (float*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(float*) * pConverter->channelsIn) + (sizeof(float) * pConverter->channelsOut * iChannelIn)));
55127 }
55128 } else {
55129 pConverter->weights.s16 = (ma_int32**)ma_offset_ptr(pHeap, heapLayout.weightsOffset);
55130 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
55131 pConverter->weights.s16[iChannelIn] = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(ma_int32*) * pConverter->channelsIn) + (sizeof(ma_int32) * pConverter->channelsOut * iChannelIn)));
55132 }
55133 }
55134
55135 /* Silence our weights by default. */
55136 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
55137 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) {
55138 if (pConverter->format == ma_format_f32) {
55139 pConverter->weights.f32[iChannelIn][iChannelOut] = 0.0f;
55140 } else {
55141 pConverter->weights.s16[iChannelIn][iChannelOut] = 0;
55142 }
55143 }
55144 }
55145
55146 /*
55147 We now need to fill out our weights table. This is determined by the mixing mode.
55148 */
55149
55150 /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */
55151 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
55152 ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);
55153
55154 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
55155 ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut);
55156
55157 if (channelPosIn == channelPosOut) {
55158 float weight = 1;
55159
55160 if (pConverter->format == ma_format_f32) {
55161 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
55162 } else {
55163 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
55164 }
55165 }
55166 }
55167 }
55168
55169 switch (pConverter->mixingMode)
55170 {
55171 case ma_channel_mix_mode_custom_weights:
55172 {
55173 if (pConfig->ppWeights == NULL) {
55174 return MA_INVALID_ARGS; /* Config specified a custom weights mixing mode, but no custom weights have been specified. */
55175 }
55176
55177 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
55178 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) {
55179 float weight = pConfig->ppWeights[iChannelIn][iChannelOut];
55180
55181 if (pConverter->format == ma_format_f32) {
55182 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
55183 } else {
55184 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
55185 }
55186 }
55187 }
55188 } break;
55189
55191 {
55192 /*
55193 In simple mode, only set weights for channels that have exactly matching types, leave the rest at
55194 zero. The 1:1 mappings have already been covered before this switch statement.
55195 */
55196 } break;
55197
55199 default:
55200 {
55201 /* Unmapped input channels. */
55202 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
55203 ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);
55204
55205 if (ma_is_spatial_channel_position(channelPosIn)) {
55206 if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) {
55207 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
55208 ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut);
55209
55210 if (ma_is_spatial_channel_position(channelPosOut)) {
55211 float weight = 0;
55212 if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
55213 weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
55214 }
55215
55216 /* Only apply the weight if we haven't already got some contribution from the respective channels. */
55217 if (pConverter->format == ma_format_f32) {
55218 if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
55219 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
55220 }
55221 } else {
55222 if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
55223 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
55224 }
55225 }
55226 }
55227 }
55228 }
55229 }
55230 }
55231
55232 /* Unmapped output channels. */
55233 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
55234 ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut);
55235
55236 if (ma_is_spatial_channel_position(channelPosOut)) {
55237 if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) {
55238 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
55239 ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);
55240
55241 if (ma_is_spatial_channel_position(channelPosIn)) {
55242 float weight = 0;
55243 if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
55244 weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
55245 }
55246
55247 /* Only apply the weight if we haven't already got some contribution from the respective channels. */
55248 if (pConverter->format == ma_format_f32) {
55249 if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
55250 pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
55251 }
55252 } else {
55253 if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
55254 pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
55255 }
55256 }
55257 }
55258 }
55259 }
55260 }
55261 }
55262
55263 /* If LFE is in the output channel map but was not present in the input channel map, configure its weight now */
55264 if (pConfig->calculateLFEFromSpatialChannels) {
55265 if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, MA_CHANNEL_LFE)) {
55266 ma_uint32 spatialChannelCount = ma_channel_map_get_spatial_channel_count(pConverter->pChannelMapIn, pConverter->channelsIn);
55267 ma_uint32 iChannelOutLFE;
55268
55269 if (spatialChannelCount > 0 && ma_channel_map_find_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, MA_CHANNEL_LFE, &iChannelOutLFE)) {
55270 const float weightForLFE = 1.0f / spatialChannelCount;
55271 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
55272 const ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);
55273 if (ma_is_spatial_channel_position(channelPosIn)) {
55274 if (pConverter->format == ma_format_f32) {
55275 if (pConverter->weights.f32[iChannelIn][iChannelOutLFE] == 0) {
55276 pConverter->weights.f32[iChannelIn][iChannelOutLFE] = weightForLFE;
55277 }
55278 } else {
55279 if (pConverter->weights.s16[iChannelIn][iChannelOutLFE] == 0) {
55280 pConverter->weights.s16[iChannelIn][iChannelOutLFE] = ma_channel_converter_float_to_fixed(weightForLFE);
55281 }
55282 }
55283 }
55284 }
55285 }
55286 }
55287 }
55288 } break;
55289 }
55290 }
55291
55292 return MA_SUCCESS;
55293}
55294
55295MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter)
55296{
55297 ma_result result;
55298 size_t heapSizeInBytes;
55299 void* pHeap;
55300
55301 result = ma_channel_converter_get_heap_size(pConfig, &heapSizeInBytes);
55302 if (result != MA_SUCCESS) {
55303 return result;
55304 }
55305
55306 if (heapSizeInBytes > 0) {
55307 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
55308 if (pHeap == NULL) {
55309 return MA_OUT_OF_MEMORY;
55310 }
55311 } else {
55312 pHeap = NULL;
55313 }
55314
55315 result = ma_channel_converter_init_preallocated(pConfig, pHeap, pConverter);
55316 if (result != MA_SUCCESS) {
55317 ma_free(pHeap, pAllocationCallbacks);
55318 return result;
55319 }
55320
55321 pConverter->_ownsHeap = MA_TRUE;
55322 return MA_SUCCESS;
55323}
55324
55325MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks)
55326{
55327 if (pConverter == NULL) {
55328 return;
55329 }
55330
55331 if (pConverter->_ownsHeap) {
55332 ma_free(pConverter->_pHeap, pAllocationCallbacks);
55333 }
55334}
55335
55336static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
55337{
55338 MA_ASSERT(pConverter != NULL);
55339 MA_ASSERT(pFramesOut != NULL);
55340 MA_ASSERT(pFramesIn != NULL);
55341
55342 ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
55343 return MA_SUCCESS;
55344}
55345
55346static ma_result ma_channel_converter_process_pcm_frames__shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
55347{
55348 MA_ASSERT(pConverter != NULL);
55349 MA_ASSERT(pFramesOut != NULL);
55350 MA_ASSERT(pFramesIn != NULL);
55351 MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut);
55352
55353 return ma_channel_map_apply_shuffle_table(pFramesOut, pConverter->channelsOut, pFramesIn, pConverter->channelsIn, frameCount, pConverter->pShuffleTable, pConverter->format);
55354}
55355
55356static ma_result ma_channel_converter_process_pcm_frames__mono_in(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
55357{
55358 ma_uint64 iFrame;
55359
55360 MA_ASSERT(pConverter != NULL);
55361 MA_ASSERT(pFramesOut != NULL);
55362 MA_ASSERT(pFramesIn != NULL);
55363 MA_ASSERT(pConverter->channelsIn == 1);
55364
55365 switch (pConverter->format)
55366 {
55367 case ma_format_u8:
55368 {
55369 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
55370 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
55371
55372 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
55373 ma_uint32 iChannel;
55374 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
55375 pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame];
55376 }
55377 }
55378 } break;
55379
55380 case ma_format_s16:
55381 {
55382 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
55383 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
55384
55385 if (pConverter->channelsOut == 2) {
55386 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
55387 pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame];
55388 pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame];
55389 }
55390 } else {
55391 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
55392 ma_uint32 iChannel;
55393 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
55394 pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame];
55395 }
55396 }
55397 }
55398 } break;
55399
55400 case ma_format_s24:
55401 {
55402 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
55403 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
55404
55405 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
55406 ma_uint32 iChannel;
55407 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
55408 ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel;
55409 ma_uint64 iSampleIn = iFrame;
55410 pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0];
55411 pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1];
55412 pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2];
55413 }
55414 }
55415 } break;
55416
55417 case ma_format_s32:
55418 {
55419 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
55420 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
55421
55422 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
55423 ma_uint32 iChannel;
55424 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
55425 pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame];
55426 }
55427 }
55428 } break;
55429
55430 case ma_format_f32:
55431 {
55432 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
55433 const float* pFramesInF32 = (const float*)pFramesIn;
55434
55435 if (pConverter->channelsOut == 2) {
55436 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
55437 pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame];
55438 pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame];
55439 }
55440 } else {
55441 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
55442 ma_uint32 iChannel;
55443 for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
55444 pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame];
55445 }
55446 }
55447 }
55448 } break;
55449
55450 default: return MA_INVALID_OPERATION; /* Unknown format. */
55451 }
55452
55453 return MA_SUCCESS;
55454}
55455
55456static ma_result ma_channel_converter_process_pcm_frames__mono_out(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
55457{
55458 ma_uint64 iFrame;
55459 ma_uint32 iChannel;
55460
55461 MA_ASSERT(pConverter != NULL);
55462 MA_ASSERT(pFramesOut != NULL);
55463 MA_ASSERT(pFramesIn != NULL);
55464 MA_ASSERT(pConverter->channelsOut == 1);
55465
55466 switch (pConverter->format)
55467 {
55468 case ma_format_u8:
55469 {
55470 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
55471 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
55472
55473 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
55474 ma_int32 t = 0;
55475 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
55476 t += ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*pConverter->channelsIn + iChannel]);
55477 }
55478
55479 pFramesOutU8[iFrame] = ma_clip_u8(t / pConverter->channelsOut);
55480 }
55481 } break;
55482
55483 case ma_format_s16:
55484 {
55485 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
55486 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
55487
55488 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
55489 ma_int32 t = 0;
55490 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
55491 t += pFramesInS16[iFrame*pConverter->channelsIn + iChannel];
55492 }
55493
55494 pFramesOutS16[iFrame] = (ma_int16)(t / pConverter->channelsIn);
55495 }
55496 } break;
55497
55498 case ma_format_s24:
55499 {
55500 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
55501 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
55502
55503 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
55504 ma_int64 t = 0;
55505 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
55506 t += ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*pConverter->channelsIn + iChannel)*3]);
55507 }
55508
55509 ma_pcm_sample_s32_to_s24_no_scale(t / pConverter->channelsIn, &pFramesOutS24[iFrame*3]);
55510 }
55511 } break;
55512
55513 case ma_format_s32:
55514 {
55515 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
55516 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
55517
55518 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
55519 ma_int64 t = 0;
55520 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
55521 t += pFramesInS32[iFrame*pConverter->channelsIn + iChannel];
55522 }
55523
55524 pFramesOutS32[iFrame] = (ma_int32)(t / pConverter->channelsIn);
55525 }
55526 } break;
55527
55528 case ma_format_f32:
55529 {
55530 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
55531 const float* pFramesInF32 = (const float*)pFramesIn;
55532
55533 for (iFrame = 0; iFrame < frameCount; ++iFrame) {
55534 float t = 0;
55535 for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
55536 t += pFramesInF32[iFrame*pConverter->channelsIn + iChannel];
55537 }
55538
55539 pFramesOutF32[iFrame] = t / pConverter->channelsIn;
55540 }
55541 } break;
55542
55543 default: return MA_INVALID_OPERATION; /* Unknown format. */
55544 }
55545
55546 return MA_SUCCESS;
55547}
55548
55549static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
55550{
55551 ma_uint32 iFrame;
55552 ma_uint32 iChannelIn;
55553 ma_uint32 iChannelOut;
55554
55555 MA_ASSERT(pConverter != NULL);
55556 MA_ASSERT(pFramesOut != NULL);
55557 MA_ASSERT(pFramesIn != NULL);
55558
55559 /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */
55560
55561 /* Clear. */
55562 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
55563
55564 /* Accumulate. */
55565 switch (pConverter->format)
55566 {
55567 case ma_format_u8:
55568 {
55569 /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
55570 const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
55571
55572 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
55573 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
55574 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
55575 ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]);
55576 ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn + iChannelIn ]);
55577 ma_int32 s = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127);
55578 pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s);
55579 }
55580 }
55581 }
55582 } break;
55583
55584 case ma_format_s16:
55585 {
55586 /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
55587 const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
55588
55589 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
55590 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
55591 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
55592 ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut];
55593 s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
55594
55595 pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767);
55596 }
55597 }
55598 }
55599 } break;
55600
55601 case ma_format_s24:
55602 {
55603 /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
55604 const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
55605
55606 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
55607 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
55608 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
55609 ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]);
55610 ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn + iChannelIn )*3]);
55611 ma_int64 s24 = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607);
55612 ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]);
55613 }
55614 }
55615 }
55616 } break;
55617
55618 case ma_format_s32:
55619 {
55620 /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
55621 const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
55622
55623 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
55624 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
55625 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
55626 ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut];
55627 s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
55628
55629 pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s);
55630 }
55631 }
55632 }
55633 } break;
55634
55635 case ma_format_f32:
55636 {
55637 /* */ float* pFramesOutF32 = ( float*)pFramesOut;
55638 const float* pFramesInF32 = (const float*)pFramesIn;
55639
55640 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
55641 for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
55642 for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
55643 pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut];
55644 }
55645 }
55646 }
55647 } break;
55648
55649 default: return MA_INVALID_OPERATION; /* Unknown format. */
55650 }
55651
55652 return MA_SUCCESS;
55653}
55654
55655MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
55656{
55657 if (pConverter == NULL) {
55658 return MA_INVALID_ARGS;
55659 }
55660
55661 if (pFramesOut == NULL) {
55662 return MA_INVALID_ARGS;
55663 }
55664
55665 if (pFramesIn == NULL) {
55666 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
55667 return MA_SUCCESS;
55668 }
55669
55670 switch (pConverter->conversionPath)
55671 {
55672 case ma_channel_conversion_path_passthrough: return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount);
55673 case ma_channel_conversion_path_mono_out: return ma_channel_converter_process_pcm_frames__mono_out(pConverter, pFramesOut, pFramesIn, frameCount);
55674 case ma_channel_conversion_path_mono_in: return ma_channel_converter_process_pcm_frames__mono_in(pConverter, pFramesOut, pFramesIn, frameCount);
55675 case ma_channel_conversion_path_shuffle: return ma_channel_converter_process_pcm_frames__shuffle(pConverter, pFramesOut, pFramesIn, frameCount);
55677 default:
55678 {
55679 return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount);
55680 }
55681 }
55682}
55683
55684MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
55685{
55686 if (pConverter == NULL || pChannelMap == NULL) {
55687 return MA_INVALID_ARGS;
55688 }
55689
55690 ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapIn, pConverter->channelsIn);
55691
55692 return MA_SUCCESS;
55693}
55694
55695MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
55696{
55697 if (pConverter == NULL || pChannelMap == NULL) {
55698 return MA_INVALID_ARGS;
55699 }
55700
55701 ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapOut, pConverter->channelsOut);
55702
55703 return MA_SUCCESS;
55704}
55705
55706
55707/**************************************************************************************************************************************************************
55708
55709Data Conversion
55710
55711**************************************************************************************************************************************************************/
55712MA_API ma_data_converter_config ma_data_converter_config_init_default(void)
55713{
55714 ma_data_converter_config config;
55715 MA_ZERO_OBJECT(&config);
55716
55719 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. */
55720
55721 /* Linear resampling defaults. */
55722 config.resampling.linear.lpfOrder = 1;
55723
55724 return config;
55725}
55726
55727MA_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)
55728{
55729 ma_data_converter_config config = ma_data_converter_config_init_default();
55730 config.formatIn = formatIn;
55731 config.formatOut = formatOut;
55732 config.channelsIn = channelsIn;
55733 config.channelsOut = channelsOut;
55734 config.sampleRateIn = sampleRateIn;
55735 config.sampleRateOut = sampleRateOut;
55736
55737 return config;
55738}
55739
55740
55741typedef struct
55742{
55743 size_t sizeInBytes;
55744 size_t channelConverterOffset;
55745 size_t resamplerOffset;
55746} ma_data_converter_heap_layout;
55747
55748static ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig)
55749{
55750 MA_ASSERT(pConfig != NULL);
55751
55752 return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut;
55753}
55754
55755static ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig)
55756{
55757 MA_ASSERT(pConfig != NULL);
55758
55759 /*
55760 We want to avoid as much data conversion as possible. The channel converter and linear
55761 resampler both support s16 and f32 natively. We need to decide on the format to use for this
55762 stage. We call this the mid format because it's used in the middle stage of the conversion
55763 pipeline. If the output format is either s16 or f32 we use that one. If that is not the case it
55764 will do the same thing for the input format. If it's neither we just use f32. If we are using a
55765 custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced
55766 to use that if resampling is required.
55767 */
55768 if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) {
55769 return ma_format_f32; /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */
55770 } else {
55771 /* */ if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) {
55772 return pConfig->formatOut;
55773 } else if (pConfig->formatIn == ma_format_s16 || pConfig->formatIn == ma_format_f32) {
55774 return pConfig->formatIn;
55775 } else {
55776 return ma_format_f32;
55777 }
55778 }
55779}
55780
55781static ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig)
55782{
55783 ma_channel_converter_config channelConverterConfig;
55784
55785 MA_ASSERT(pConfig != NULL);
55786
55787 channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode);
55788 channelConverterConfig.ppWeights = pConfig->ppChannelWeights;
55789 channelConverterConfig.calculateLFEFromSpatialChannels = pConfig->calculateLFEFromSpatialChannels;
55790
55791 return channelConverterConfig;
55792}
55793
55794static ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig)
55795{
55796 ma_resampler_config resamplerConfig;
55797 ma_uint32 resamplerChannels;
55798
55799 MA_ASSERT(pConfig != NULL);
55800
55801 /* 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. */
55802 if (pConfig->channelsIn < pConfig->channelsOut) {
55803 resamplerChannels = pConfig->channelsIn;
55804 } else {
55805 resamplerChannels = pConfig->channelsOut;
55806 }
55807
55808 resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm);
55809 resamplerConfig.linear = pConfig->resampling.linear;
55810 resamplerConfig.pBackendVTable = pConfig->resampling.pBackendVTable;
55811 resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData;
55812
55813 return resamplerConfig;
55814}
55815
55816static ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout)
55817{
55818 ma_result result;
55819
55820 MA_ASSERT(pHeapLayout != NULL);
55821
55822 MA_ZERO_OBJECT(pHeapLayout);
55823
55824 if (pConfig == NULL) {
55825 return MA_INVALID_ARGS;
55826 }
55827
55828 if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
55829 return MA_INVALID_ARGS;
55830 }
55831
55832 pHeapLayout->sizeInBytes = 0;
55833
55834 /* Channel converter. */
55835 pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes;
55836 {
55837 size_t heapSizeInBytes;
55838 ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig);
55839
55840 result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes);
55841 if (result != MA_SUCCESS) {
55842 return result;
55843 }
55844
55845 pHeapLayout->sizeInBytes += heapSizeInBytes;
55846 }
55847
55848 /* Resampler. */
55849 pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes;
55850 if (ma_data_converter_config_is_resampler_required(pConfig)) {
55851 size_t heapSizeInBytes;
55852 ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig);
55853
55854 result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes);
55855 if (result != MA_SUCCESS) {
55856 return result;
55857 }
55858
55859 pHeapLayout->sizeInBytes += heapSizeInBytes;
55860 }
55861
55862 /* Make sure allocation size is aligned. */
55863 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
55864
55865 return MA_SUCCESS;
55866}
55867
55868MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes)
55869{
55870 ma_result result;
55871 ma_data_converter_heap_layout heapLayout;
55872
55873 if (pHeapSizeInBytes == NULL) {
55874 return MA_INVALID_ARGS;
55875 }
55876
55877 *pHeapSizeInBytes = 0;
55878
55879 result = ma_data_converter_get_heap_layout(pConfig, &heapLayout);
55880 if (result != MA_SUCCESS) {
55881 return result;
55882 }
55883
55884 *pHeapSizeInBytes = heapLayout.sizeInBytes;
55885
55886 return MA_SUCCESS;
55887}
55888
55889MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter)
55890{
55891 ma_result result;
55892 ma_data_converter_heap_layout heapLayout;
55893 ma_format midFormat;
55894 ma_bool32 isResamplingRequired;
55895
55896 if (pConverter == NULL) {
55897 return MA_INVALID_ARGS;
55898 }
55899
55900 MA_ZERO_OBJECT(pConverter);
55901
55902 result = ma_data_converter_get_heap_layout(pConfig, &heapLayout);
55903 if (result != MA_SUCCESS) {
55904 return result;
55905 }
55906
55907 pConverter->_pHeap = pHeap;
55908 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
55909
55910 pConverter->formatIn = pConfig->formatIn;
55911 pConverter->formatOut = pConfig->formatOut;
55912 pConverter->channelsIn = pConfig->channelsIn;
55913 pConverter->channelsOut = pConfig->channelsOut;
55914 pConverter->sampleRateIn = pConfig->sampleRateIn;
55915 pConverter->sampleRateOut = pConfig->sampleRateOut;
55916 pConverter->ditherMode = pConfig->ditherMode;
55917
55918 /*
55919 Determine if resampling is required. We need to do this so we can determine an appropriate
55920 mid format to use. If resampling is required, the mid format must be ma_format_f32 since
55921 that is the only one that is guaranteed to supported by custom resampling backends.
55922 */
55923 isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig);
55924 midFormat = ma_data_converter_config_get_mid_format(pConfig);
55925
55926
55927 /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */
55928 {
55929 ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig);
55930
55931 result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter);
55932 if (result != MA_SUCCESS) {
55933 return result;
55934 }
55935
55936 /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */
55938 pConverter->hasChannelConverter = MA_TRUE;
55939 }
55940 }
55941
55942
55943 /* Resampler. */
55944 if (isResamplingRequired) {
55945 ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig);
55946
55947 result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler);
55948 if (result != MA_SUCCESS) {
55949 return result;
55950 }
55951
55952 pConverter->hasResampler = MA_TRUE;
55953 }
55954
55955
55956 /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */
55957 if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) {
55958 /* 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. */
55959 if (pConverter->formatIn == pConverter->formatOut) {
55960 /* The formats are the same so we can just pass through. */
55961 pConverter->hasPreFormatConversion = MA_FALSE;
55962 pConverter->hasPostFormatConversion = MA_FALSE;
55963 } else {
55964 /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */
55965 pConverter->hasPreFormatConversion = MA_FALSE;
55966 pConverter->hasPostFormatConversion = MA_TRUE;
55967 }
55968 } else {
55969 /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */
55970 if (pConverter->formatIn != midFormat) {
55971 pConverter->hasPreFormatConversion = MA_TRUE;
55972 }
55973 if (pConverter->formatOut != midFormat) {
55974 pConverter->hasPostFormatConversion = MA_TRUE;
55975 }
55976 }
55977
55978 /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */
55979 if (pConverter->hasPreFormatConversion == MA_FALSE &&
55980 pConverter->hasPostFormatConversion == MA_FALSE &&
55981 pConverter->hasChannelConverter == MA_FALSE &&
55982 pConverter->hasResampler == MA_FALSE) {
55983 pConverter->isPassthrough = MA_TRUE;
55984 }
55985
55986
55987 /* We now need to determine our execution path. */
55988 if (pConverter->isPassthrough) {
55990 } else {
55991 if (pConverter->channelsIn < pConverter->channelsOut) {
55992 /* Do resampling first, if necessary. */
55993 MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE);
55994
55995 if (pConverter->hasResampler) {
55997 } else {
55999 }
56000 } else {
56001 /* Do channel conversion first, if necessary. */
56002 if (pConverter->hasChannelConverter) {
56003 if (pConverter->hasResampler) {
56005 } else {
56007 }
56008 } else {
56009 /* Channel routing not required. */
56010 if (pConverter->hasResampler) {
56012 } else {
56014 }
56015 }
56016 }
56017 }
56018
56019 return MA_SUCCESS;
56020}
56021
56022MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter)
56023{
56024 ma_result result;
56025 size_t heapSizeInBytes;
56026 void* pHeap;
56027
56028 result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes);
56029 if (result != MA_SUCCESS) {
56030 return result;
56031 }
56032
56033 if (heapSizeInBytes > 0) {
56034 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
56035 if (pHeap == NULL) {
56036 return MA_OUT_OF_MEMORY;
56037 }
56038 } else {
56039 pHeap = NULL;
56040 }
56041
56042 result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter);
56043 if (result != MA_SUCCESS) {
56044 ma_free(pHeap, pAllocationCallbacks);
56045 return result;
56046 }
56047
56048 pConverter->_ownsHeap = MA_TRUE;
56049 return MA_SUCCESS;
56050}
56051
56052MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks)
56053{
56054 if (pConverter == NULL) {
56055 return;
56056 }
56057
56058 if (pConverter->hasResampler) {
56059 ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks);
56060 }
56061
56062 ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks);
56063
56064 if (pConverter->_ownsHeap) {
56065 ma_free(pConverter->_pHeap, pAllocationCallbacks);
56066 }
56067}
56068
56069static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
56070{
56071 ma_uint64 frameCountIn;
56072 ma_uint64 frameCountOut;
56073 ma_uint64 frameCount;
56074
56075 MA_ASSERT(pConverter != NULL);
56076
56077 frameCountIn = 0;
56078 if (pFrameCountIn != NULL) {
56079 frameCountIn = *pFrameCountIn;
56080 }
56081
56082 frameCountOut = 0;
56083 if (pFrameCountOut != NULL) {
56084 frameCountOut = *pFrameCountOut;
56085 }
56086
56087 frameCount = ma_min(frameCountIn, frameCountOut);
56088
56089 if (pFramesOut != NULL) {
56090 if (pFramesIn != NULL) {
56091 ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
56092 } else {
56093 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
56094 }
56095 }
56096
56097 if (pFrameCountIn != NULL) {
56098 *pFrameCountIn = frameCount;
56099 }
56100 if (pFrameCountOut != NULL) {
56101 *pFrameCountOut = frameCount;
56102 }
56103
56104 return MA_SUCCESS;
56105}
56106
56107static 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)
56108{
56109 ma_uint64 frameCountIn;
56110 ma_uint64 frameCountOut;
56111 ma_uint64 frameCount;
56112
56113 MA_ASSERT(pConverter != NULL);
56114
56115 frameCountIn = 0;
56116 if (pFrameCountIn != NULL) {
56117 frameCountIn = *pFrameCountIn;
56118 }
56119
56120 frameCountOut = 0;
56121 if (pFrameCountOut != NULL) {
56122 frameCountOut = *pFrameCountOut;
56123 }
56124
56125 frameCount = ma_min(frameCountIn, frameCountOut);
56126
56127 if (pFramesOut != NULL) {
56128 if (pFramesIn != NULL) {
56129 ma_convert_pcm_frames_format(pFramesOut, pConverter->formatOut, pFramesIn, pConverter->formatIn, frameCount, pConverter->channelsIn, pConverter->ditherMode);
56130 } else {
56131 ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
56132 }
56133 }
56134
56135 if (pFrameCountIn != NULL) {
56136 *pFrameCountIn = frameCount;
56137 }
56138 if (pFrameCountOut != NULL) {
56139 *pFrameCountOut = frameCount;
56140 }
56141
56142 return MA_SUCCESS;
56143}
56144
56145
56146static 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)
56147{
56148 ma_result result = MA_SUCCESS;
56149 ma_uint64 frameCountIn;
56150 ma_uint64 frameCountOut;
56151 ma_uint64 framesProcessedIn;
56152 ma_uint64 framesProcessedOut;
56153
56154 MA_ASSERT(pConverter != NULL);
56155
56156 frameCountIn = 0;
56157 if (pFrameCountIn != NULL) {
56158 frameCountIn = *pFrameCountIn;
56159 }
56160
56161 frameCountOut = 0;
56162 if (pFrameCountOut != NULL) {
56163 frameCountOut = *pFrameCountOut;
56164 }
56165
56166 framesProcessedIn = 0;
56167 framesProcessedOut = 0;
56168
56169 while (framesProcessedOut < frameCountOut) {
56170 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
56171 const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
56172 const void* pFramesInThisIteration;
56173 /* */ void* pFramesOutThisIteration;
56174 ma_uint64 frameCountInThisIteration;
56175 ma_uint64 frameCountOutThisIteration;
56176
56177 if (pFramesIn != NULL) {
56178 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
56179 } else {
56180 pFramesInThisIteration = NULL;
56181 }
56182
56183 if (pFramesOut != NULL) {
56184 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
56185 } else {
56186 pFramesOutThisIteration = NULL;
56187 }
56188
56189 /* Do a pre format conversion if necessary. */
56190 if (pConverter->hasPreFormatConversion) {
56191 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
56192 const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
56193
56194 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
56195 if (frameCountInThisIteration > tempBufferInCap) {
56196 frameCountInThisIteration = tempBufferInCap;
56197 }
56198
56199 if (pConverter->hasPostFormatConversion) {
56200 if (frameCountInThisIteration > tempBufferOutCap) {
56201 frameCountInThisIteration = tempBufferOutCap;
56202 }
56203 }
56204
56205 if (pFramesInThisIteration != NULL) {
56206 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pFramesInThisIteration, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
56207 } else {
56208 MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
56209 }
56210
56211 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
56212
56213 if (pConverter->hasPostFormatConversion) {
56214 /* Both input and output conversion required. Output to the temp buffer. */
56215 if (frameCountOutThisIteration > tempBufferOutCap) {
56216 frameCountOutThisIteration = tempBufferOutCap;
56217 }
56218
56219 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
56220 } else {
56221 /* Only pre-format required. Output straight to the output buffer. */
56222 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration);
56223 }
56224
56225 if (result != MA_SUCCESS) {
56226 break;
56227 }
56228 } else {
56229 /* No pre-format required. Just read straight from the input buffer. */
56230 MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
56231
56232 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
56233 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
56234 if (frameCountOutThisIteration > tempBufferOutCap) {
56235 frameCountOutThisIteration = tempBufferOutCap;
56236 }
56237
56238 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
56239 if (result != MA_SUCCESS) {
56240 break;
56241 }
56242 }
56243
56244 /* If we are doing a post format conversion we need to do that now. */
56245 if (pConverter->hasPostFormatConversion) {
56246 if (pFramesOutThisIteration != NULL) {
56247 ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->resampler.channels, pConverter->ditherMode);
56248 }
56249 }
56250
56251 framesProcessedIn += frameCountInThisIteration;
56252 framesProcessedOut += frameCountOutThisIteration;
56253
56254 MA_ASSERT(framesProcessedIn <= frameCountIn);
56255 MA_ASSERT(framesProcessedOut <= frameCountOut);
56256
56257 if (frameCountOutThisIteration == 0) {
56258 break; /* Consumed all of our input data. */
56259 }
56260 }
56261
56262 if (pFrameCountIn != NULL) {
56263 *pFrameCountIn = framesProcessedIn;
56264 }
56265 if (pFrameCountOut != NULL) {
56266 *pFrameCountOut = framesProcessedOut;
56267 }
56268
56269 return result;
56270}
56271
56272static 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)
56273{
56274 MA_ASSERT(pConverter != NULL);
56275
56276 if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
56277 /* Neither pre- nor post-format required. This is simple case where only resampling is required. */
56278 return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
56279 } else {
56280 /* Format conversion required. */
56281 return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
56282 }
56283}
56284
56285static 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)
56286{
56287 ma_result result;
56288 ma_uint64 frameCountIn;
56289 ma_uint64 frameCountOut;
56290 ma_uint64 frameCount;
56291
56292 MA_ASSERT(pConverter != NULL);
56293
56294 frameCountIn = 0;
56295 if (pFrameCountIn != NULL) {
56296 frameCountIn = *pFrameCountIn;
56297 }
56298
56299 frameCountOut = 0;
56300 if (pFrameCountOut != NULL) {
56301 frameCountOut = *pFrameCountOut;
56302 }
56303
56304 frameCount = ma_min(frameCountIn, frameCountOut);
56305
56306 if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
56307 /* No format conversion required. */
56308 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount);
56309 if (result != MA_SUCCESS) {
56310 return result;
56311 }
56312 } else {
56313 /* Format conversion required. */
56314 ma_uint64 framesProcessed = 0;
56315
56316 while (framesProcessed < frameCount) {
56317 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
56318 const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
56319 const void* pFramesInThisIteration;
56320 /* */ void* pFramesOutThisIteration;
56321 ma_uint64 frameCountThisIteration;
56322
56323 if (pFramesIn != NULL) {
56324 pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
56325 } else {
56326 pFramesInThisIteration = NULL;
56327 }
56328
56329 if (pFramesOut != NULL) {
56330 pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
56331 } else {
56332 pFramesOutThisIteration = NULL;
56333 }
56334
56335 /* Do a pre format conversion if necessary. */
56336 if (pConverter->hasPreFormatConversion) {
56337 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
56338 const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
56339
56340 frameCountThisIteration = (frameCount - framesProcessed);
56341 if (frameCountThisIteration > tempBufferInCap) {
56342 frameCountThisIteration = tempBufferInCap;
56343 }
56344
56345 if (pConverter->hasPostFormatConversion) {
56346 if (frameCountThisIteration > tempBufferOutCap) {
56347 frameCountThisIteration = tempBufferOutCap;
56348 }
56349 }
56350
56351 if (pFramesInThisIteration != NULL) {
56352 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->formatIn, frameCountThisIteration, pConverter->channelsIn, pConverter->ditherMode);
56353 } else {
56354 MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
56355 }
56356
56357 if (pConverter->hasPostFormatConversion) {
56358 /* Both input and output conversion required. Output to the temp buffer. */
56359 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration);
56360 } else {
56361 /* Only pre-format required. Output straight to the output buffer. */
56362 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration);
56363 }
56364
56365 if (result != MA_SUCCESS) {
56366 break;
56367 }
56368 } else {
56369 /* No pre-format required. Just read straight from the input buffer. */
56370 MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
56371
56372 frameCountThisIteration = (frameCount - framesProcessed);
56373 if (frameCountThisIteration > tempBufferOutCap) {
56374 frameCountThisIteration = tempBufferOutCap;
56375 }
56376
56377 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration);
56378 if (result != MA_SUCCESS) {
56379 break;
56380 }
56381 }
56382
56383 /* If we are doing a post format conversion we need to do that now. */
56384 if (pConverter->hasPostFormatConversion) {
56385 if (pFramesOutThisIteration != NULL) {
56386 ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode);
56387 }
56388 }
56389
56390 framesProcessed += frameCountThisIteration;
56391 }
56392 }
56393
56394 if (pFrameCountIn != NULL) {
56395 *pFrameCountIn = frameCount;
56396 }
56397 if (pFrameCountOut != NULL) {
56398 *pFrameCountOut = frameCount;
56399 }
56400
56401 return MA_SUCCESS;
56402}
56403
56404static 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)
56405{
56406 ma_result result;
56407 ma_uint64 frameCountIn;
56408 ma_uint64 frameCountOut;
56409 ma_uint64 framesProcessedIn;
56410 ma_uint64 framesProcessedOut;
56411 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
56412 ma_uint64 tempBufferInCap;
56413 ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
56414 ma_uint64 tempBufferMidCap;
56415 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
56416 ma_uint64 tempBufferOutCap;
56417
56418 MA_ASSERT(pConverter != NULL);
56419 MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format);
56420 MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn);
56421 MA_ASSERT(pConverter->resampler.channels < pConverter->channelConverter.channelsOut);
56422
56423 frameCountIn = 0;
56424 if (pFrameCountIn != NULL) {
56425 frameCountIn = *pFrameCountIn;
56426 }
56427
56428 frameCountOut = 0;
56429 if (pFrameCountOut != NULL) {
56430 frameCountOut = *pFrameCountOut;
56431 }
56432
56433 framesProcessedIn = 0;
56434 framesProcessedOut = 0;
56435
56436 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
56437 tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
56438 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
56439
56440 while (framesProcessedOut < frameCountOut) {
56441 ma_uint64 frameCountInThisIteration;
56442 ma_uint64 frameCountOutThisIteration;
56443 const void* pRunningFramesIn = NULL;
56444 void* pRunningFramesOut = NULL;
56445 const void* pResampleBufferIn;
56446 void* pChannelsBufferOut;
56447
56448 if (pFramesIn != NULL) {
56449 pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
56450 }
56451 if (pFramesOut != NULL) {
56452 pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
56453 }
56454
56455 /* Run input data through the resampler and output it to the temporary buffer. */
56456 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
56457
56458 if (pConverter->hasPreFormatConversion) {
56459 if (frameCountInThisIteration > tempBufferInCap) {
56460 frameCountInThisIteration = tempBufferInCap;
56461 }
56462 }
56463
56464 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
56465 if (frameCountOutThisIteration > tempBufferMidCap) {
56466 frameCountOutThisIteration = tempBufferMidCap;
56467 }
56468
56469 /* We can't read more frames than can fit in the output buffer. */
56470 if (pConverter->hasPostFormatConversion) {
56471 if (frameCountOutThisIteration > tempBufferOutCap) {
56472 frameCountOutThisIteration = tempBufferOutCap;
56473 }
56474 }
56475
56476 /* 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. */
56477
56478 /*
56479 We need to try to predict how many input frames will be required for the resampler. If the
56480 resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further
56481 off we are from this, the more wasted format conversions we'll end up doing.
56482 */
56483 #if 1
56484 {
56485 ma_uint64 requiredInputFrameCount;
56486
56487 result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount);
56488 if (result != MA_SUCCESS) {
56489 /* Fall back to a best guess. */
56490 requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;
56491 }
56492
56493 if (frameCountInThisIteration > requiredInputFrameCount) {
56494 frameCountInThisIteration = requiredInputFrameCount;
56495 }
56496 }
56497 #endif
56498
56499 if (pConverter->hasPreFormatConversion) {
56500 if (pFramesIn != NULL) {
56501 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
56502 pResampleBufferIn = pTempBufferIn;
56503 } else {
56504 pResampleBufferIn = NULL;
56505 }
56506 } else {
56507 pResampleBufferIn = pRunningFramesIn;
56508 }
56509
56510 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration);
56511 if (result != MA_SUCCESS) {
56512 return result;
56513 }
56514
56515
56516 /*
56517 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
56518 this part if we have an output buffer.
56519 */
56520 if (pFramesOut != NULL) {
56521 if (pConverter->hasPostFormatConversion) {
56522 pChannelsBufferOut = pTempBufferOut;
56523 } else {
56524 pChannelsBufferOut = pRunningFramesOut;
56525 }
56526
56527 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration);
56528 if (result != MA_SUCCESS) {
56529 return result;
56530 }
56531
56532 /* Finally we do post format conversion. */
56533 if (pConverter->hasPostFormatConversion) {
56534 ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode);
56535 }
56536 }
56537
56538
56539 framesProcessedIn += frameCountInThisIteration;
56540 framesProcessedOut += frameCountOutThisIteration;
56541
56542 MA_ASSERT(framesProcessedIn <= frameCountIn);
56543 MA_ASSERT(framesProcessedOut <= frameCountOut);
56544
56545 if (frameCountOutThisIteration == 0) {
56546 break; /* Consumed all of our input data. */
56547 }
56548 }
56549
56550 if (pFrameCountIn != NULL) {
56551 *pFrameCountIn = framesProcessedIn;
56552 }
56553 if (pFrameCountOut != NULL) {
56554 *pFrameCountOut = framesProcessedOut;
56555 }
56556
56557 return MA_SUCCESS;
56558}
56559
56560static 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)
56561{
56562 ma_result result;
56563 ma_uint64 frameCountIn;
56564 ma_uint64 frameCountOut;
56565 ma_uint64 framesProcessedIn;
56566 ma_uint64 framesProcessedOut;
56567 ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */
56568 ma_uint64 tempBufferInCap;
56569 ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */
56570 ma_uint64 tempBufferMidCap;
56571 ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */
56572 ma_uint64 tempBufferOutCap;
56573
56574 MA_ASSERT(pConverter != NULL);
56575 MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format);
56576 MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut);
56577 MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn);
56578
56579 frameCountIn = 0;
56580 if (pFrameCountIn != NULL) {
56581 frameCountIn = *pFrameCountIn;
56582 }
56583
56584 frameCountOut = 0;
56585 if (pFrameCountOut != NULL) {
56586 frameCountOut = *pFrameCountOut;
56587 }
56588
56589 framesProcessedIn = 0;
56590 framesProcessedOut = 0;
56591
56592 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
56593 tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
56594 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
56595
56596 while (framesProcessedOut < frameCountOut) {
56597 ma_uint64 frameCountInThisIteration;
56598 ma_uint64 frameCountOutThisIteration;
56599 const void* pRunningFramesIn = NULL;
56600 void* pRunningFramesOut = NULL;
56601 const void* pChannelsBufferIn;
56602 void* pResampleBufferOut;
56603
56604 if (pFramesIn != NULL) {
56605 pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
56606 }
56607 if (pFramesOut != NULL) {
56608 pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
56609 }
56610
56611 /*
56612 Before doing any processing we need to determine how many frames we should try processing
56613 this iteration, for both input and output. The resampler requires us to perform format and
56614 channel conversion before passing any data into it. If we get our input count wrong, we'll
56615 end up performing redundant pre-processing. This isn't the end of the world, but it does
56616 result in some inefficiencies proportionate to how far our estimates are off.
56617
56618 If the resampler has a means to calculate exactly how much we'll need, we'll use that.
56619 Otherwise we'll make a best guess. In order to do this, we'll need to calculate the output
56620 frame count first.
56621 */
56622 frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
56623 if (frameCountOutThisIteration > tempBufferMidCap) {
56624 frameCountOutThisIteration = tempBufferMidCap;
56625 }
56626
56627 if (pConverter->hasPostFormatConversion) {
56628 if (frameCountOutThisIteration > tempBufferOutCap) {
56629 frameCountOutThisIteration = tempBufferOutCap;
56630 }
56631 }
56632
56633 /* Now that we have the output frame count we can determine the input frame count. */
56634 frameCountInThisIteration = (frameCountIn - framesProcessedIn);
56635 if (pConverter->hasPreFormatConversion) {
56636 if (frameCountInThisIteration > tempBufferInCap) {
56637 frameCountInThisIteration = tempBufferInCap;
56638 }
56639 }
56640
56641 if (frameCountInThisIteration > tempBufferMidCap) {
56642 frameCountInThisIteration = tempBufferMidCap;
56643 }
56644
56645 #if 1
56646 {
56647 ma_uint64 requiredInputFrameCount;
56648
56649 result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount);
56650 if (result != MA_SUCCESS) {
56651 /* Fall back to a best guess. */
56652 requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;
56653 }
56654
56655 if (frameCountInThisIteration > requiredInputFrameCount) {
56656 frameCountInThisIteration = requiredInputFrameCount;
56657 }
56658 }
56659 #endif
56660
56661
56662 /* Pre format conversion. */
56663 if (pConverter->hasPreFormatConversion) {
56664 if (pRunningFramesIn != NULL) {
56665 ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
56666 pChannelsBufferIn = pTempBufferIn;
56667 } else {
56668 pChannelsBufferIn = NULL;
56669 }
56670 } else {
56671 pChannelsBufferIn = pRunningFramesIn;
56672 }
56673
56674
56675 /* Channel conversion. */
56676 result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration);
56677 if (result != MA_SUCCESS) {
56678 return result;
56679 }
56680
56681
56682 /* Resampling. */
56683 if (pConverter->hasPostFormatConversion) {
56684 pResampleBufferOut = pTempBufferOut;
56685 } else {
56686 pResampleBufferOut = pRunningFramesOut;
56687 }
56688
56689 result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration);
56690 if (result != MA_SUCCESS) {
56691 return result;
56692 }
56693
56694
56695 /* Post format conversion. */
56696 if (pConverter->hasPostFormatConversion) {
56697 if (pRunningFramesOut != NULL) {
56698 ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode);
56699 }
56700 }
56701
56702
56703 framesProcessedIn += frameCountInThisIteration;
56704 framesProcessedOut += frameCountOutThisIteration;
56705
56706 MA_ASSERT(framesProcessedIn <= frameCountIn);
56707 MA_ASSERT(framesProcessedOut <= frameCountOut);
56708
56709 if (frameCountOutThisIteration == 0) {
56710 break; /* Consumed all of our input data. */
56711 }
56712 }
56713
56714 if (pFrameCountIn != NULL) {
56715 *pFrameCountIn = framesProcessedIn;
56716 }
56717 if (pFrameCountOut != NULL) {
56718 *pFrameCountOut = framesProcessedOut;
56719 }
56720
56721 return MA_SUCCESS;
56722}
56723
56724MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
56725{
56726 if (pConverter == NULL) {
56727 return MA_INVALID_ARGS;
56728 }
56729
56730 switch (pConverter->executionPath)
56731 {
56732 case ma_data_converter_execution_path_passthrough: return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
56733 case ma_data_converter_execution_path_format_only: return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
56734 case ma_data_converter_execution_path_channels_only: return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
56735 case ma_data_converter_execution_path_resample_only: return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
56736 case ma_data_converter_execution_path_resample_first: return ma_data_converter_process_pcm_frames__resample_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
56737 case ma_data_converter_execution_path_channels_first: return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
56738 default: return MA_INVALID_OPERATION; /* Should never hit this. */
56739 }
56740}
56741
56742MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
56743{
56744 if (pConverter == NULL) {
56745 return MA_INVALID_ARGS;
56746 }
56747
56748 if (pConverter->hasResampler == MA_FALSE) {
56749 return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */
56750 }
56751
56752 return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut);
56753}
56754
56755MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut)
56756{
56757 if (pConverter == NULL) {
56758 return MA_INVALID_ARGS;
56759 }
56760
56761 if (pConverter->hasResampler == MA_FALSE) {
56762 return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */
56763 }
56764
56765 return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut);
56766}
56767
56768MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter)
56769{
56770 if (pConverter == NULL) {
56771 return 0;
56772 }
56773
56774 if (pConverter->hasResampler) {
56775 return ma_resampler_get_input_latency(&pConverter->resampler);
56776 }
56777
56778 return 0; /* No latency without a resampler. */
56779}
56780
56781MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter)
56782{
56783 if (pConverter == NULL) {
56784 return 0;
56785 }
56786
56787 if (pConverter->hasResampler) {
56788 return ma_resampler_get_output_latency(&pConverter->resampler);
56789 }
56790
56791 return 0; /* No latency without a resampler. */
56792}
56793
56794MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
56795{
56796 if (pInputFrameCount == NULL) {
56797 return MA_INVALID_ARGS;
56798 }
56799
56800 *pInputFrameCount = 0;
56801
56802 if (pConverter == NULL) {
56803 return MA_INVALID_ARGS;
56804 }
56805
56806 if (pConverter->hasResampler) {
56807 return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount);
56808 } else {
56809 *pInputFrameCount = outputFrameCount; /* 1:1 */
56810 return MA_SUCCESS;
56811 }
56812}
56813
56814MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
56815{
56816 if (pOutputFrameCount == NULL) {
56817 return MA_INVALID_ARGS;
56818 }
56819
56820 *pOutputFrameCount = 0;
56821
56822 if (pConverter == NULL) {
56823 return MA_INVALID_ARGS;
56824 }
56825
56826 if (pConverter->hasResampler) {
56827 return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount);
56828 } else {
56829 *pOutputFrameCount = inputFrameCount; /* 1:1 */
56830 return MA_SUCCESS;
56831 }
56832}
56833
56834MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
56835{
56836 if (pConverter == NULL || pChannelMap == NULL) {
56837 return MA_INVALID_ARGS;
56838 }
56839
56840 if (pConverter->hasChannelConverter) {
56841 ma_channel_converter_get_output_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap);
56842 } else {
56843 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsOut);
56844 }
56845
56846 return MA_SUCCESS;
56847}
56848
56849MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
56850{
56851 if (pConverter == NULL || pChannelMap == NULL) {
56852 return MA_INVALID_ARGS;
56853 }
56854
56855 if (pConverter->hasChannelConverter) {
56856 ma_channel_converter_get_input_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap);
56857 } else {
56858 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsIn);
56859 }
56860
56861 return MA_SUCCESS;
56862}
56863
56864MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter)
56865{
56866 if (pConverter == NULL) {
56867 return MA_INVALID_ARGS;
56868 }
56869
56870 /* There's nothing to do if we're not resampling. */
56871 if (pConverter->hasResampler == MA_FALSE) {
56872 return MA_SUCCESS;
56873 }
56874
56875 return ma_resampler_reset(&pConverter->resampler);
56876}
56877
56878
56879
56880/**************************************************************************************************************************************************************
56881
56882Channel Maps
56883
56884**************************************************************************************************************************************************************/
56885static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex);
56886
56887MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)
56888{
56889 if (pChannelMap == NULL) {
56890 return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex);
56891 } else {
56892 if (channelIndex >= channelCount) {
56893 return MA_CHANNEL_NONE;
56894 }
56895
56896 return pChannelMap[channelIndex];
56897 }
56898}
56899
56900MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels)
56901{
56902 if (pChannelMap == NULL) {
56903 return;
56904 }
56905
56906 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels);
56907}
56908
56909
56910static ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex)
56911{
56912 if (channelCount == 0 || channelIndex >= channelCount) {
56913 return MA_CHANNEL_NONE;
56914 }
56915
56916 /* 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 */
56917 switch (channelCount)
56918 {
56919 case 0: return MA_CHANNEL_NONE;
56920
56921 case 1:
56922 {
56923 return MA_CHANNEL_MONO;
56924 } break;
56925
56926 case 2:
56927 {
56928 switch (channelIndex) {
56929 case 0: return MA_CHANNEL_FRONT_LEFT;
56930 case 1: return MA_CHANNEL_FRONT_RIGHT;
56931 }
56932 } break;
56933
56934 case 3: /* No defined, but best guess. */
56935 {
56936 switch (channelIndex) {
56937 case 0: return MA_CHANNEL_FRONT_LEFT;
56938 case 1: return MA_CHANNEL_FRONT_RIGHT;
56939 case 2: return MA_CHANNEL_FRONT_CENTER;
56940 }
56941 } break;
56942
56943 case 4:
56944 {
56945 switch (channelIndex) {
56946 #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP
56947 /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */
56948 case 0: return MA_CHANNEL_FRONT_LEFT;
56949 case 1: return MA_CHANNEL_FRONT_RIGHT;
56950 case 2: return MA_CHANNEL_FRONT_CENTER;
56951 case 3: return MA_CHANNEL_BACK_CENTER;
56952 #else
56953 /* Quad. */
56954 case 0: return MA_CHANNEL_FRONT_LEFT;
56955 case 1: return MA_CHANNEL_FRONT_RIGHT;
56956 case 2: return MA_CHANNEL_BACK_LEFT;
56957 case 3: return MA_CHANNEL_BACK_RIGHT;
56958 #endif
56959 }
56960 } break;
56961
56962 case 5: /* Not defined, but best guess. */
56963 {
56964 switch (channelIndex) {
56965 case 0: return MA_CHANNEL_FRONT_LEFT;
56966 case 1: return MA_CHANNEL_FRONT_RIGHT;
56967 case 2: return MA_CHANNEL_FRONT_CENTER;
56968 case 3: return MA_CHANNEL_BACK_LEFT;
56969 case 4: return MA_CHANNEL_BACK_RIGHT;
56970 }
56971 } break;
56972
56973 case 6:
56974 {
56975 switch (channelIndex) {
56976 case 0: return MA_CHANNEL_FRONT_LEFT;
56977 case 1: return MA_CHANNEL_FRONT_RIGHT;
56978 case 2: return MA_CHANNEL_FRONT_CENTER;
56979 case 3: return MA_CHANNEL_LFE;
56980 case 4: return MA_CHANNEL_SIDE_LEFT;
56981 case 5: return MA_CHANNEL_SIDE_RIGHT;
56982 }
56983 } break;
56984
56985 case 7: /* Not defined, but best guess. */
56986 {
56987 switch (channelIndex) {
56988 case 0: return MA_CHANNEL_FRONT_LEFT;
56989 case 1: return MA_CHANNEL_FRONT_RIGHT;
56990 case 2: return MA_CHANNEL_FRONT_CENTER;
56991 case 3: return MA_CHANNEL_LFE;
56992 case 4: return MA_CHANNEL_BACK_CENTER;
56993 case 5: return MA_CHANNEL_SIDE_LEFT;
56994 case 6: return MA_CHANNEL_SIDE_RIGHT;
56995 }
56996 } break;
56997
56998 case 8:
56999 default:
57000 {
57001 switch (channelIndex) {
57002 case 0: return MA_CHANNEL_FRONT_LEFT;
57003 case 1: return MA_CHANNEL_FRONT_RIGHT;
57004 case 2: return MA_CHANNEL_FRONT_CENTER;
57005 case 3: return MA_CHANNEL_LFE;
57006 case 4: return MA_CHANNEL_BACK_LEFT;
57007 case 5: return MA_CHANNEL_BACK_RIGHT;
57008 case 6: return MA_CHANNEL_SIDE_LEFT;
57009 case 7: return MA_CHANNEL_SIDE_RIGHT;
57010 }
57011 } break;
57012 }
57013
57014 if (channelCount > 8) {
57015 if (channelIndex < 32) { /* We have 32 AUX channels. */
57016 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
57017 }
57018 }
57019
57020 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
57021 return MA_CHANNEL_NONE;
57022}
57023
57024static ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex)
57025{
57026 switch (channelCount)
57027 {
57028 case 0: return MA_CHANNEL_NONE;
57029
57030 case 1:
57031 {
57032 return MA_CHANNEL_MONO;
57033 } break;
57034
57035 case 2:
57036 {
57037 switch (channelIndex) {
57038 case 0: return MA_CHANNEL_FRONT_LEFT;
57039 case 1: return MA_CHANNEL_FRONT_RIGHT;
57040 }
57041 } break;
57042
57043 case 3:
57044 {
57045 switch (channelIndex) {
57046 case 0: return MA_CHANNEL_FRONT_LEFT;
57047 case 1: return MA_CHANNEL_FRONT_RIGHT;
57048 case 2: return MA_CHANNEL_FRONT_CENTER;
57049 }
57050 } break;
57051
57052 case 4:
57053 {
57054 switch (channelIndex) {
57055 case 0: return MA_CHANNEL_FRONT_LEFT;
57056 case 1: return MA_CHANNEL_FRONT_RIGHT;
57057 case 2: return MA_CHANNEL_BACK_LEFT;
57058 case 3: return MA_CHANNEL_BACK_RIGHT;
57059 }
57060 } break;
57061
57062 case 5:
57063 {
57064 switch (channelIndex) {
57065 case 0: return MA_CHANNEL_FRONT_LEFT;
57066 case 1: return MA_CHANNEL_FRONT_RIGHT;
57067 case 2: return MA_CHANNEL_BACK_LEFT;
57068 case 3: return MA_CHANNEL_BACK_RIGHT;
57069 case 4: return MA_CHANNEL_FRONT_CENTER;
57070 }
57071 } break;
57072
57073 case 6:
57074 {
57075 switch (channelIndex) {
57076 case 0: return MA_CHANNEL_FRONT_LEFT;
57077 case 1: return MA_CHANNEL_FRONT_RIGHT;
57078 case 2: return MA_CHANNEL_BACK_LEFT;
57079 case 3: return MA_CHANNEL_BACK_RIGHT;
57080 case 4: return MA_CHANNEL_FRONT_CENTER;
57081 case 5: return MA_CHANNEL_LFE;
57082 }
57083 } break;
57084
57085 case 7:
57086 {
57087 switch (channelIndex) {
57088 case 0: return MA_CHANNEL_FRONT_LEFT;
57089 case 1: return MA_CHANNEL_FRONT_RIGHT;
57090 case 2: return MA_CHANNEL_BACK_LEFT;
57091 case 3: return MA_CHANNEL_BACK_RIGHT;
57092 case 4: return MA_CHANNEL_FRONT_CENTER;
57093 case 5: return MA_CHANNEL_LFE;
57094 case 6: return MA_CHANNEL_BACK_CENTER;
57095 }
57096 } break;
57097
57098 case 8:
57099 default:
57100 {
57101 switch (channelIndex) {
57102 case 0: return MA_CHANNEL_FRONT_LEFT;
57103 case 1: return MA_CHANNEL_FRONT_RIGHT;
57104 case 2: return MA_CHANNEL_BACK_LEFT;
57105 case 3: return MA_CHANNEL_BACK_RIGHT;
57106 case 4: return MA_CHANNEL_FRONT_CENTER;
57107 case 5: return MA_CHANNEL_LFE;
57108 case 6: return MA_CHANNEL_SIDE_LEFT;
57109 case 7: return MA_CHANNEL_SIDE_RIGHT;
57110 }
57111 } break;
57112 }
57113
57114 if (channelCount > 8) {
57115 if (channelIndex < 32) { /* We have 32 AUX channels. */
57116 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
57117 }
57118 }
57119
57120 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
57121 return MA_CHANNEL_NONE;
57122}
57123
57124static ma_channel ma_channel_map_init_standard_channel_rfc3551(ma_uint32 channelCount, ma_uint32 channelIndex)
57125{
57126 switch (channelCount)
57127 {
57128 case 0: return MA_CHANNEL_NONE;
57129
57130 case 1:
57131 {
57132 return MA_CHANNEL_MONO;
57133 } break;
57134
57135 case 2:
57136 {
57137 switch (channelIndex) {
57138 case 0: return MA_CHANNEL_FRONT_LEFT;
57139 case 1: return MA_CHANNEL_FRONT_RIGHT;
57140 }
57141 } break;
57142
57143 case 3:
57144 {
57145 switch (channelIndex) {
57146 case 0: return MA_CHANNEL_FRONT_LEFT;
57147 case 1: return MA_CHANNEL_FRONT_RIGHT;
57148 case 2: return MA_CHANNEL_FRONT_CENTER;
57149 }
57150 } break;
57151
57152 case 4:
57153 {
57154 switch (channelIndex) {
57155 case 0: return MA_CHANNEL_FRONT_LEFT;
57156 case 2: return MA_CHANNEL_FRONT_CENTER;
57157 case 1: return MA_CHANNEL_FRONT_RIGHT;
57158 case 3: return MA_CHANNEL_BACK_CENTER;
57159 }
57160 } break;
57161
57162 case 5:
57163 {
57164 switch (channelIndex) {
57165 case 0: return MA_CHANNEL_FRONT_LEFT;
57166 case 1: return MA_CHANNEL_FRONT_RIGHT;
57167 case 2: return MA_CHANNEL_FRONT_CENTER;
57168 case 3: return MA_CHANNEL_BACK_LEFT;
57169 case 4: return MA_CHANNEL_BACK_RIGHT;
57170 }
57171 } break;
57172
57173 case 6:
57174 default:
57175 {
57176 switch (channelIndex) {
57177 case 0: return MA_CHANNEL_FRONT_LEFT;
57178 case 1: return MA_CHANNEL_SIDE_LEFT;
57179 case 2: return MA_CHANNEL_FRONT_CENTER;
57180 case 3: return MA_CHANNEL_FRONT_RIGHT;
57181 case 4: return MA_CHANNEL_SIDE_RIGHT;
57182 case 5: return MA_CHANNEL_BACK_CENTER;
57183 }
57184 } break;
57185 }
57186
57187 if (channelCount > 6) {
57188 if (channelIndex < 32) { /* We have 32 AUX channels. */
57189 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6));
57190 }
57191 }
57192
57193 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
57194 return MA_CHANNEL_NONE;
57195}
57196
57197static ma_channel ma_channel_map_init_standard_channel_flac(ma_uint32 channelCount, ma_uint32 channelIndex)
57198{
57199 switch (channelCount)
57200 {
57201 case 0: return MA_CHANNEL_NONE;
57202
57203 case 1:
57204 {
57205 return MA_CHANNEL_MONO;
57206 } break;
57207
57208 case 2:
57209 {
57210 switch (channelIndex) {
57211 case 0: return MA_CHANNEL_FRONT_LEFT;
57212 case 1: return MA_CHANNEL_FRONT_RIGHT;
57213 }
57214 } break;
57215
57216 case 3:
57217 {
57218 switch (channelIndex) {
57219 case 0: return MA_CHANNEL_FRONT_LEFT;
57220 case 1: return MA_CHANNEL_FRONT_RIGHT;
57221 case 2: return MA_CHANNEL_FRONT_CENTER;
57222 }
57223 } break;
57224
57225 case 4:
57226 {
57227 switch (channelIndex) {
57228 case 0: return MA_CHANNEL_FRONT_LEFT;
57229 case 1: return MA_CHANNEL_FRONT_RIGHT;
57230 case 2: return MA_CHANNEL_BACK_LEFT;
57231 case 3: return MA_CHANNEL_BACK_RIGHT;
57232 }
57233 } break;
57234
57235 case 5:
57236 {
57237 switch (channelIndex) {
57238 case 0: return MA_CHANNEL_FRONT_LEFT;
57239 case 1: return MA_CHANNEL_FRONT_RIGHT;
57240 case 2: return MA_CHANNEL_FRONT_CENTER;
57241 case 3: return MA_CHANNEL_BACK_LEFT;
57242 case 4: return MA_CHANNEL_BACK_RIGHT;
57243 }
57244 } break;
57245
57246 case 6:
57247 {
57248 switch (channelIndex) {
57249 case 0: return MA_CHANNEL_FRONT_LEFT;
57250 case 1: return MA_CHANNEL_FRONT_RIGHT;
57251 case 2: return MA_CHANNEL_FRONT_CENTER;
57252 case 3: return MA_CHANNEL_LFE;
57253 case 4: return MA_CHANNEL_BACK_LEFT;
57254 case 5: return MA_CHANNEL_BACK_RIGHT;
57255 }
57256 } break;
57257
57258 case 7:
57259 {
57260 switch (channelIndex) {
57261 case 0: return MA_CHANNEL_FRONT_LEFT;
57262 case 1: return MA_CHANNEL_FRONT_RIGHT;
57263 case 2: return MA_CHANNEL_FRONT_CENTER;
57264 case 3: return MA_CHANNEL_LFE;
57265 case 4: return MA_CHANNEL_BACK_CENTER;
57266 case 5: return MA_CHANNEL_SIDE_LEFT;
57267 case 6: return MA_CHANNEL_SIDE_RIGHT;
57268 }
57269 } break;
57270
57271 case 8:
57272 default:
57273 {
57274 switch (channelIndex) {
57275 case 0: return MA_CHANNEL_FRONT_LEFT;
57276 case 1: return MA_CHANNEL_FRONT_RIGHT;
57277 case 2: return MA_CHANNEL_FRONT_CENTER;
57278 case 3: return MA_CHANNEL_LFE;
57279 case 4: return MA_CHANNEL_BACK_LEFT;
57280 case 5: return MA_CHANNEL_BACK_RIGHT;
57281 case 6: return MA_CHANNEL_SIDE_LEFT;
57282 case 7: return MA_CHANNEL_SIDE_RIGHT;
57283 }
57284 } break;
57285 }
57286
57287 if (channelCount > 8) {
57288 if (channelIndex < 32) { /* We have 32 AUX channels. */
57289 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
57290 }
57291 }
57292
57293 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
57294 return MA_CHANNEL_NONE;
57295}
57296
57297static ma_channel ma_channel_map_init_standard_channel_vorbis(ma_uint32 channelCount, ma_uint32 channelIndex)
57298{
57299 switch (channelCount)
57300 {
57301 case 0: return MA_CHANNEL_NONE;
57302
57303 case 1:
57304 {
57305 return MA_CHANNEL_MONO;
57306 } break;
57307
57308 case 2:
57309 {
57310 switch (channelIndex) {
57311 case 0: return MA_CHANNEL_FRONT_LEFT;
57312 case 1: return MA_CHANNEL_FRONT_RIGHT;
57313 }
57314 } break;
57315
57316 case 3:
57317 {
57318 switch (channelIndex) {
57319 case 0: return MA_CHANNEL_FRONT_LEFT;
57320 case 1: return MA_CHANNEL_FRONT_CENTER;
57321 case 2: return MA_CHANNEL_FRONT_RIGHT;
57322 }
57323 } break;
57324
57325 case 4:
57326 {
57327 switch (channelIndex) {
57328 case 0: return MA_CHANNEL_FRONT_LEFT;
57329 case 1: return MA_CHANNEL_FRONT_RIGHT;
57330 case 2: return MA_CHANNEL_BACK_LEFT;
57331 case 3: return MA_CHANNEL_BACK_RIGHT;
57332 }
57333 } break;
57334
57335 case 5:
57336 {
57337 switch (channelIndex) {
57338 case 0: return MA_CHANNEL_FRONT_LEFT;
57339 case 1: return MA_CHANNEL_FRONT_CENTER;
57340 case 2: return MA_CHANNEL_FRONT_RIGHT;
57341 case 3: return MA_CHANNEL_BACK_LEFT;
57342 case 4: return MA_CHANNEL_BACK_RIGHT;
57343 }
57344 } break;
57345
57346 case 6:
57347 {
57348 switch (channelIndex) {
57349 case 0: return MA_CHANNEL_FRONT_LEFT;
57350 case 1: return MA_CHANNEL_FRONT_CENTER;
57351 case 2: return MA_CHANNEL_FRONT_RIGHT;
57352 case 3: return MA_CHANNEL_BACK_LEFT;
57353 case 4: return MA_CHANNEL_BACK_RIGHT;
57354 case 5: return MA_CHANNEL_LFE;
57355 }
57356 } break;
57357
57358 case 7:
57359 {
57360 switch (channelIndex) {
57361 case 0: return MA_CHANNEL_FRONT_LEFT;
57362 case 1: return MA_CHANNEL_FRONT_CENTER;
57363 case 2: return MA_CHANNEL_FRONT_RIGHT;
57364 case 3: return MA_CHANNEL_SIDE_LEFT;
57365 case 4: return MA_CHANNEL_SIDE_RIGHT;
57366 case 5: return MA_CHANNEL_BACK_CENTER;
57367 case 6: return MA_CHANNEL_LFE;
57368 }
57369 } break;
57370
57371 case 8:
57372 default:
57373 {
57374 switch (channelIndex) {
57375 case 0: return MA_CHANNEL_FRONT_LEFT;
57376 case 1: return MA_CHANNEL_FRONT_CENTER;
57377 case 2: return MA_CHANNEL_FRONT_RIGHT;
57378 case 3: return MA_CHANNEL_SIDE_LEFT;
57379 case 4: return MA_CHANNEL_SIDE_RIGHT;
57380 case 5: return MA_CHANNEL_BACK_LEFT;
57381 case 6: return MA_CHANNEL_BACK_RIGHT;
57382 case 7: return MA_CHANNEL_LFE;
57383 }
57384 } break;
57385 }
57386
57387 if (channelCount > 8) {
57388 if (channelIndex < 32) { /* We have 32 AUX channels. */
57389 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
57390 }
57391 }
57392
57393 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
57394 return MA_CHANNEL_NONE;
57395}
57396
57397static ma_channel ma_channel_map_init_standard_channel_sound4(ma_uint32 channelCount, ma_uint32 channelIndex)
57398{
57399 switch (channelCount)
57400 {
57401 case 0: return MA_CHANNEL_NONE;
57402
57403 case 1:
57404 {
57405 return MA_CHANNEL_MONO;
57406 } break;
57407
57408 case 2:
57409 {
57410 switch (channelIndex) {
57411 case 0: return MA_CHANNEL_FRONT_LEFT;
57412 case 1: return MA_CHANNEL_FRONT_RIGHT;
57413 }
57414 } break;
57415
57416 case 3:
57417 {
57418 switch (channelIndex) {
57419 case 0: return MA_CHANNEL_FRONT_LEFT;
57420 case 1: return MA_CHANNEL_FRONT_RIGHT;
57421 case 2: return MA_CHANNEL_FRONT_CENTER;
57422 }
57423 } break;
57424
57425 case 4:
57426 {
57427 switch (channelIndex) {
57428 case 0: return MA_CHANNEL_FRONT_LEFT;
57429 case 1: return MA_CHANNEL_FRONT_RIGHT;
57430 case 2: return MA_CHANNEL_BACK_LEFT;
57431 case 3: return MA_CHANNEL_BACK_RIGHT;
57432 }
57433 } break;
57434
57435 case 5:
57436 {
57437 switch (channelIndex) {
57438 case 0: return MA_CHANNEL_FRONT_LEFT;
57439 case 1: return MA_CHANNEL_FRONT_RIGHT;
57440 case 2: return MA_CHANNEL_FRONT_CENTER;
57441 case 3: return MA_CHANNEL_BACK_LEFT;
57442 case 4: return MA_CHANNEL_BACK_RIGHT;
57443 }
57444 } break;
57445
57446 case 6:
57447 {
57448 switch (channelIndex) {
57449 case 0: return MA_CHANNEL_FRONT_LEFT;
57450 case 1: return MA_CHANNEL_FRONT_CENTER;
57451 case 2: return MA_CHANNEL_FRONT_RIGHT;
57452 case 3: return MA_CHANNEL_BACK_LEFT;
57453 case 4: return MA_CHANNEL_BACK_RIGHT;
57454 case 5: return MA_CHANNEL_LFE;
57455 }
57456 } break;
57457
57458 case 7:
57459 {
57460 switch (channelIndex) {
57461 case 0: return MA_CHANNEL_FRONT_LEFT;
57462 case 1: return MA_CHANNEL_FRONT_CENTER;
57463 case 2: return MA_CHANNEL_FRONT_RIGHT;
57464 case 3: return MA_CHANNEL_SIDE_LEFT;
57465 case 4: return MA_CHANNEL_SIDE_RIGHT;
57466 case 5: return MA_CHANNEL_BACK_CENTER;
57467 case 6: return MA_CHANNEL_LFE;
57468 }
57469 } break;
57470
57471 case 8:
57472 default:
57473 {
57474 switch (channelIndex) {
57475 case 0: return MA_CHANNEL_FRONT_LEFT;
57476 case 1: return MA_CHANNEL_FRONT_CENTER;
57477 case 2: return MA_CHANNEL_FRONT_RIGHT;
57478 case 3: return MA_CHANNEL_SIDE_LEFT;
57479 case 4: return MA_CHANNEL_SIDE_RIGHT;
57480 case 5: return MA_CHANNEL_BACK_LEFT;
57481 case 6: return MA_CHANNEL_BACK_RIGHT;
57482 case 7: return MA_CHANNEL_LFE;
57483 }
57484 } break;
57485 }
57486
57487 if (channelCount > 8) {
57488 if (channelIndex < 32) { /* We have 32 AUX channels. */
57489 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
57490 }
57491 }
57492
57493 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
57494 return MA_CHANNEL_NONE;
57495}
57496
57497static ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex)
57498{
57499 switch (channelCount)
57500 {
57501 case 0: return MA_CHANNEL_NONE;
57502
57503 case 1:
57504 {
57505 return MA_CHANNEL_MONO;
57506 } break;
57507
57508 case 2:
57509 {
57510 switch (channelIndex) {
57511 case 0: return MA_CHANNEL_FRONT_LEFT;
57512 case 1: return MA_CHANNEL_FRONT_RIGHT;
57513 }
57514 } break;
57515
57516 case 3: /* No defined, but best guess. */
57517 {
57518 switch (channelIndex) {
57519 case 0: return MA_CHANNEL_FRONT_LEFT;
57520 case 1: return MA_CHANNEL_FRONT_RIGHT;
57521 case 2: return MA_CHANNEL_FRONT_CENTER;
57522 }
57523 } break;
57524
57525 case 4:
57526 {
57527 switch (channelIndex) {
57528 case 0: return MA_CHANNEL_FRONT_LEFT;
57529 case 1: return MA_CHANNEL_FRONT_RIGHT;
57530 case 2: return MA_CHANNEL_BACK_LEFT;
57531 case 3: return MA_CHANNEL_BACK_RIGHT;
57532 }
57533 } break;
57534
57535 case 5: /* Not defined, but best guess. */
57536 {
57537 switch (channelIndex) {
57538 case 0: return MA_CHANNEL_FRONT_LEFT;
57539 case 1: return MA_CHANNEL_FRONT_RIGHT;
57540 case 2: return MA_CHANNEL_BACK_LEFT;
57541 case 3: return MA_CHANNEL_BACK_RIGHT;
57542 case 4: return MA_CHANNEL_FRONT_CENTER;
57543 }
57544 } break;
57545
57546 case 6:
57547 default:
57548 {
57549 switch (channelIndex) {
57550 case 0: return MA_CHANNEL_FRONT_LEFT;
57551 case 1: return MA_CHANNEL_FRONT_RIGHT;
57552 case 2: return MA_CHANNEL_BACK_LEFT;
57553 case 3: return MA_CHANNEL_BACK_RIGHT;
57554 case 4: return MA_CHANNEL_FRONT_CENTER;
57555 case 5: return MA_CHANNEL_LFE;
57556 }
57557 } break;
57558 }
57559
57560 if (channelCount > 6) {
57561 if (channelIndex < 32) { /* We have 32 AUX channels. */
57562 return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6));
57563 }
57564 }
57565
57566 /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */
57567 return MA_CHANNEL_NONE;
57568}
57569
57570
57571static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)
57572{
57573 if (channelCount == 0 || channelIndex >= channelCount) {
57574 return MA_CHANNEL_NONE;
57575 }
57576
57577 switch (standardChannelMap)
57578 {
57580 {
57581 return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex);
57582 } break;
57583
57585 {
57586 return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex);
57587 } break;
57588
57590 {
57591 return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex);
57592 } break;
57593
57595 {
57596 return ma_channel_map_init_standard_channel_vorbis(channelCount, channelIndex);
57597 } break;
57598
57600 {
57601 return ma_channel_map_init_standard_channel_sound4(channelCount, channelIndex);
57602 } break;
57603
57605 {
57606 return ma_channel_map_init_standard_channel_sndio(channelCount, channelIndex);
57607 } break;
57608
57609 case ma_standard_channel_map_microsoft: /* Also default. */
57610 /*case ma_standard_channel_map_default;*/
57611 default:
57612 {
57613 return ma_channel_map_init_standard_channel_microsoft(channelCount, channelIndex);
57614 } break;
57615 }
57616}
57617
57618MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels)
57619{
57620 ma_uint32 iChannel;
57621
57622 if (pChannelMap == NULL || channelMapCap == 0 || channels == 0) {
57623 return;
57624 }
57625
57626 for (iChannel = 0; iChannel < channels; iChannel += 1) {
57627 if (channelMapCap == 0) {
57628 break; /* Ran out of room. */
57629 }
57630
57631 pChannelMap[0] = ma_channel_map_init_standard_channel(standardChannelMap, channels, iChannel);
57632 pChannelMap += 1;
57633 channelMapCap -= 1;
57634 }
57635}
57636
57637MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels)
57638{
57639 if (pOut != NULL && pIn != NULL && channels > 0) {
57640 MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels);
57641 }
57642}
57643
57644MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels)
57645{
57646 if (pOut == NULL || channels == 0) {
57647 return;
57648 }
57649
57650 if (pIn != NULL) {
57651 ma_channel_map_copy(pOut, pIn, channels);
57652 } else {
57653 ma_channel_map_init_standard(ma_standard_channel_map_default, pOut, channelMapCapOut, channels);
57654 }
57655}
57656
57657MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels)
57658{
57659 /* A channel count of 0 is invalid. */
57660 if (channels == 0) {
57661 return MA_FALSE;
57662 }
57663
57664 /* It does not make sense to have a mono channel when there is more than 1 channel. */
57665 if (channels > 1) {
57666 ma_uint32 iChannel;
57667 for (iChannel = 0; iChannel < channels; ++iChannel) {
57668 if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == MA_CHANNEL_MONO) {
57669 return MA_FALSE;
57670 }
57671 }
57672 }
57673
57674 return MA_TRUE;
57675}
57676
57677MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels)
57678{
57679 ma_uint32 iChannel;
57680
57681 if (pChannelMapA == pChannelMapB) {
57682 return MA_TRUE;
57683 }
57684
57685 for (iChannel = 0; iChannel < channels; ++iChannel) {
57686 if (ma_channel_map_get_channel(pChannelMapA, channels, iChannel) != ma_channel_map_get_channel(pChannelMapB, channels, iChannel)) {
57687 return MA_FALSE;
57688 }
57689 }
57690
57691 return MA_TRUE;
57692}
57693
57694MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels)
57695{
57696 ma_uint32 iChannel;
57697
57698 /* A null channel map is equivalent to the default channel map. */
57699 if (pChannelMap == NULL) {
57700 return MA_FALSE;
57701 }
57702
57703 for (iChannel = 0; iChannel < channels; ++iChannel) {
57704 if (pChannelMap[iChannel] != MA_CHANNEL_NONE) {
57705 return MA_FALSE;
57706 }
57707 }
57708
57709 return MA_TRUE;
57710}
57711
57712MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition)
57713{
57714 return ma_channel_map_find_channel_position(channels, pChannelMap, channelPosition, NULL);
57715}
57716
57717MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex)
57718{
57719 ma_uint32 iChannel;
57720
57721 if (pChannelIndex != NULL) {
57722 *pChannelIndex = (ma_uint32)-1;
57723 }
57724
57725 for (iChannel = 0; iChannel < channels; ++iChannel) {
57726 if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) {
57727 if (pChannelIndex != NULL) {
57728 *pChannelIndex = iChannel;
57729 }
57730
57731 return MA_TRUE;
57732 }
57733 }
57734
57735 /* Getting here means the channel position was not found. */
57736 return MA_FALSE;
57737}
57738
57739MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap)
57740{
57741 size_t len;
57742 ma_uint32 iChannel;
57743
57744 len = 0;
57745
57746 for (iChannel = 0; iChannel < channels; iChannel += 1) {
57747 const char* pChannelStr = ma_channel_position_to_string(ma_channel_map_get_channel(pChannelMap, channels, iChannel));
57748 size_t channelStrLen = strlen(pChannelStr);
57749
57750 /* Append the string if necessary. */
57751 if (pBufferOut != NULL && bufferCap > len + channelStrLen) {
57752 MA_COPY_MEMORY(pBufferOut + len, pChannelStr, channelStrLen);
57753 }
57754 len += channelStrLen;
57755
57756 /* Append a space if it's not the last item. */
57757 if (iChannel+1 < channels) {
57758 if (pBufferOut != NULL && bufferCap > len + 1) {
57759 pBufferOut[len] = ' ';
57760 }
57761 len += 1;
57762 }
57763 }
57764
57765 /* Null terminate. Don't increment the length here. */
57766 if (pBufferOut != NULL) {
57767 if (bufferCap > len) {
57768 pBufferOut[len] = '\0';
57769 } else if (bufferCap > 0) {
57770 pBufferOut[bufferCap - 1] = '\0';
57771 }
57772 }
57773
57774 return len;
57775}
57776
57778{
57779 switch (channel)
57780 {
57781 case MA_CHANNEL_NONE : return "CHANNEL_NONE";
57782 case MA_CHANNEL_MONO : return "CHANNEL_MONO";
57783 case MA_CHANNEL_FRONT_LEFT : return "CHANNEL_FRONT_LEFT";
57784 case MA_CHANNEL_FRONT_RIGHT : return "CHANNEL_FRONT_RIGHT";
57785 case MA_CHANNEL_FRONT_CENTER : return "CHANNEL_FRONT_CENTER";
57786 case MA_CHANNEL_LFE : return "CHANNEL_LFE";
57787 case MA_CHANNEL_BACK_LEFT : return "CHANNEL_BACK_LEFT";
57788 case MA_CHANNEL_BACK_RIGHT : return "CHANNEL_BACK_RIGHT";
57789 case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER";
57790 case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER";
57791 case MA_CHANNEL_BACK_CENTER : return "CHANNEL_BACK_CENTER";
57792 case MA_CHANNEL_SIDE_LEFT : return "CHANNEL_SIDE_LEFT";
57793 case MA_CHANNEL_SIDE_RIGHT : return "CHANNEL_SIDE_RIGHT";
57794 case MA_CHANNEL_TOP_CENTER : return "CHANNEL_TOP_CENTER";
57795 case MA_CHANNEL_TOP_FRONT_LEFT : return "CHANNEL_TOP_FRONT_LEFT";
57796 case MA_CHANNEL_TOP_FRONT_CENTER : return "CHANNEL_TOP_FRONT_CENTER";
57797 case MA_CHANNEL_TOP_FRONT_RIGHT : return "CHANNEL_TOP_FRONT_RIGHT";
57798 case MA_CHANNEL_TOP_BACK_LEFT : return "CHANNEL_TOP_BACK_LEFT";
57799 case MA_CHANNEL_TOP_BACK_CENTER : return "CHANNEL_TOP_BACK_CENTER";
57800 case MA_CHANNEL_TOP_BACK_RIGHT : return "CHANNEL_TOP_BACK_RIGHT";
57801 case MA_CHANNEL_AUX_0 : return "CHANNEL_AUX_0";
57802 case MA_CHANNEL_AUX_1 : return "CHANNEL_AUX_1";
57803 case MA_CHANNEL_AUX_2 : return "CHANNEL_AUX_2";
57804 case MA_CHANNEL_AUX_3 : return "CHANNEL_AUX_3";
57805 case MA_CHANNEL_AUX_4 : return "CHANNEL_AUX_4";
57806 case MA_CHANNEL_AUX_5 : return "CHANNEL_AUX_5";
57807 case MA_CHANNEL_AUX_6 : return "CHANNEL_AUX_6";
57808 case MA_CHANNEL_AUX_7 : return "CHANNEL_AUX_7";
57809 case MA_CHANNEL_AUX_8 : return "CHANNEL_AUX_8";
57810 case MA_CHANNEL_AUX_9 : return "CHANNEL_AUX_9";
57811 case MA_CHANNEL_AUX_10 : return "CHANNEL_AUX_10";
57812 case MA_CHANNEL_AUX_11 : return "CHANNEL_AUX_11";
57813 case MA_CHANNEL_AUX_12 : return "CHANNEL_AUX_12";
57814 case MA_CHANNEL_AUX_13 : return "CHANNEL_AUX_13";
57815 case MA_CHANNEL_AUX_14 : return "CHANNEL_AUX_14";
57816 case MA_CHANNEL_AUX_15 : return "CHANNEL_AUX_15";
57817 case MA_CHANNEL_AUX_16 : return "CHANNEL_AUX_16";
57818 case MA_CHANNEL_AUX_17 : return "CHANNEL_AUX_17";
57819 case MA_CHANNEL_AUX_18 : return "CHANNEL_AUX_18";
57820 case MA_CHANNEL_AUX_19 : return "CHANNEL_AUX_19";
57821 case MA_CHANNEL_AUX_20 : return "CHANNEL_AUX_20";
57822 case MA_CHANNEL_AUX_21 : return "CHANNEL_AUX_21";
57823 case MA_CHANNEL_AUX_22 : return "CHANNEL_AUX_22";
57824 case MA_CHANNEL_AUX_23 : return "CHANNEL_AUX_23";
57825 case MA_CHANNEL_AUX_24 : return "CHANNEL_AUX_24";
57826 case MA_CHANNEL_AUX_25 : return "CHANNEL_AUX_25";
57827 case MA_CHANNEL_AUX_26 : return "CHANNEL_AUX_26";
57828 case MA_CHANNEL_AUX_27 : return "CHANNEL_AUX_27";
57829 case MA_CHANNEL_AUX_28 : return "CHANNEL_AUX_28";
57830 case MA_CHANNEL_AUX_29 : return "CHANNEL_AUX_29";
57831 case MA_CHANNEL_AUX_30 : return "CHANNEL_AUX_30";
57832 case MA_CHANNEL_AUX_31 : return "CHANNEL_AUX_31";
57833 default: break;
57834 }
57835
57836 return "UNKNOWN";
57837}
57838
57839
57840
57841/**************************************************************************************************************************************************************
57842
57843Conversion Helpers
57844
57845**************************************************************************************************************************************************************/
57846MA_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)
57847{
57848 ma_data_converter_config config;
57849
57850 config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut);
57851 config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
57852
57853 return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config);
57854}
57855
57856MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig)
57857{
57858 ma_result result;
57859 ma_data_converter converter;
57860
57861 if (frameCountIn == 0 || pConfig == NULL) {
57862 return 0;
57863 }
57864
57865 result = ma_data_converter_init(pConfig, NULL, &converter);
57866 if (result != MA_SUCCESS) {
57867 return 0; /* Failed to initialize the data converter. */
57868 }
57869
57870 if (pOut == NULL) {
57871 result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut);
57872 if (result != MA_SUCCESS) {
57873 if (result == MA_NOT_IMPLEMENTED) {
57874 /* No way to calculate the number of frames, so we'll need to brute force it and loop. */
57875 frameCountOut = 0;
57876
57877 while (frameCountIn > 0) {
57878 ma_uint64 framesProcessedIn = frameCountIn;
57879 ma_uint64 framesProcessedOut = 0xFFFFFFFF;
57880
57881 result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut);
57882 if (result != MA_SUCCESS) {
57883 break;
57884 }
57885
57886 frameCountIn -= framesProcessedIn;
57887 }
57888 }
57889 }
57890 } else {
57891 result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut);
57892 if (result != MA_SUCCESS) {
57893 frameCountOut = 0;
57894 }
57895 }
57896
57897 ma_data_converter_uninit(&converter, NULL);
57898 return frameCountOut;
57899}
57900
57901
57902/**************************************************************************************************************************************************************
57903
57904Ring Buffer
57905
57906**************************************************************************************************************************************************************/
57907static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset)
57908{
57909 return encodedOffset & 0x7FFFFFFF;
57910}
57911
57912static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset)
57913{
57914 return encodedOffset & 0x80000000;
57915}
57916
57917static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB)
57918{
57919 MA_ASSERT(pRB != NULL);
57920 return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedReadOffset)));
57921}
57922
57923static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB)
57924{
57925 MA_ASSERT(pRB != NULL);
57926 return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedWriteOffset)));
57927}
57928
57929static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag)
57930{
57931 return offsetLoopFlag | offsetInBytes;
57932}
57933
57934static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag)
57935{
57936 MA_ASSERT(pOffsetInBytes != NULL);
57937 MA_ASSERT(pOffsetLoopFlag != NULL);
57938
57939 *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset);
57940 *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset);
57941}
57942
57943
57944MA_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)
57945{
57946 ma_result result;
57947 const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1);
57948
57949 if (pRB == NULL) {
57950 return MA_INVALID_ARGS;
57951 }
57952
57953 if (subbufferSizeInBytes == 0 || subbufferCount == 0) {
57954 return MA_INVALID_ARGS;
57955 }
57956
57957 if (subbufferSizeInBytes > maxSubBufferSize) {
57958 return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */
57959 }
57960
57961
57962 MA_ZERO_OBJECT(pRB);
57963
57964 result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks);
57965 if (result != MA_SUCCESS) {
57966 return result;
57967 }
57968
57969 pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes;
57970 pRB->subbufferCount = (ma_uint32)subbufferCount;
57971
57972 if (pOptionalPreallocatedBuffer != NULL) {
57973 pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes;
57974 pRB->pBuffer = pOptionalPreallocatedBuffer;
57975 } else {
57976 size_t bufferSizeInBytes;
57977
57978 /*
57979 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
57980 we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT.
57981 */
57983
57984 bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes;
57985 pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks);
57986 if (pRB->pBuffer == NULL) {
57987 return MA_OUT_OF_MEMORY;
57988 }
57989
57990 MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes);
57991 pRB->ownsBuffer = MA_TRUE;
57992 }
57993
57994 return MA_SUCCESS;
57995}
57996
57997MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB)
57998{
57999 return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
58000}
58001
58002MA_API void ma_rb_uninit(ma_rb* pRB)
58003{
58004 if (pRB == NULL) {
58005 return;
58006 }
58007
58008 if (pRB->ownsBuffer) {
58010 }
58011}
58012
58013MA_API void ma_rb_reset(ma_rb* pRB)
58014{
58015 if (pRB == NULL) {
58016 return;
58017 }
58018
58019 ma_atomic_exchange_32(&pRB->encodedReadOffset, 0);
58020 ma_atomic_exchange_32(&pRB->encodedWriteOffset, 0);
58021}
58022
58023MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
58024{
58025 ma_uint32 writeOffset;
58026 ma_uint32 writeOffsetInBytes;
58027 ma_uint32 writeOffsetLoopFlag;
58028 ma_uint32 readOffset;
58029 ma_uint32 readOffsetInBytes;
58030 ma_uint32 readOffsetLoopFlag;
58031 size_t bytesAvailable;
58032 size_t bytesRequested;
58033
58034 if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
58035 return MA_INVALID_ARGS;
58036 }
58037
58038 /* The returned buffer should never move ahead of the write pointer. */
58039 writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
58040 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
58041
58042 readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
58043 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
58044
58045 /*
58046 The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we
58047 can only read up to the write pointer. If not, we can only read up to the end of the buffer.
58048 */
58049 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
58050 bytesAvailable = writeOffsetInBytes - readOffsetInBytes;
58051 } else {
58052 bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes;
58053 }
58054
58055 bytesRequested = *pSizeInBytes;
58056 if (bytesRequested > bytesAvailable) {
58057 bytesRequested = bytesAvailable;
58058 }
58059
58060 *pSizeInBytes = bytesRequested;
58061 (*ppBufferOut) = ma_rb__get_read_ptr(pRB);
58062
58063 return MA_SUCCESS;
58064}
58065
58066MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes)
58067{
58068 ma_uint32 readOffset;
58069 ma_uint32 readOffsetInBytes;
58070 ma_uint32 readOffsetLoopFlag;
58071 ma_uint32 newReadOffsetInBytes;
58072 ma_uint32 newReadOffsetLoopFlag;
58073
58074 if (pRB == NULL) {
58075 return MA_INVALID_ARGS;
58076 }
58077
58078 readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
58079 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
58080
58081 /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
58082 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes);
58083 if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) {
58084 return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
58085 }
58086
58087 /* Move the read pointer back to the start if necessary. */
58088 newReadOffsetLoopFlag = readOffsetLoopFlag;
58089 if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) {
58090 newReadOffsetInBytes = 0;
58091 newReadOffsetLoopFlag ^= 0x80000000;
58092 }
58093
58094 ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag));
58095
58096 return MA_SUCCESS;
58097}
58098
58099MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
58100{
58101 ma_uint32 readOffset;
58102 ma_uint32 readOffsetInBytes;
58103 ma_uint32 readOffsetLoopFlag;
58104 ma_uint32 writeOffset;
58105 ma_uint32 writeOffsetInBytes;
58106 ma_uint32 writeOffsetLoopFlag;
58107 size_t bytesAvailable;
58108 size_t bytesRequested;
58109
58110 if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
58111 return MA_INVALID_ARGS;
58112 }
58113
58114 /* The returned buffer should never overtake the read buffer. */
58115 readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
58116 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
58117
58118 writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
58119 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
58120
58121 /*
58122 In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only
58123 write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should
58124 never overtake the read pointer.
58125 */
58126 if (writeOffsetLoopFlag == readOffsetLoopFlag) {
58127 bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes;
58128 } else {
58129 bytesAvailable = readOffsetInBytes - writeOffsetInBytes;
58130 }
58131
58132 bytesRequested = *pSizeInBytes;
58133 if (bytesRequested > bytesAvailable) {
58134 bytesRequested = bytesAvailable;
58135 }
58136
58137 *pSizeInBytes = bytesRequested;
58138 *ppBufferOut = ma_rb__get_write_ptr(pRB);
58139
58140 /* Clear the buffer if desired. */
58141 if (pRB->clearOnWriteAcquire) {
58142 MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes);
58143 }
58144
58145 return MA_SUCCESS;
58146}
58147
58148MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes)
58149{
58150 ma_uint32 writeOffset;
58151 ma_uint32 writeOffsetInBytes;
58152 ma_uint32 writeOffsetLoopFlag;
58153 ma_uint32 newWriteOffsetInBytes;
58154 ma_uint32 newWriteOffsetLoopFlag;
58155
58156 if (pRB == NULL) {
58157 return MA_INVALID_ARGS;
58158 }
58159
58160 writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
58161 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
58162
58163 /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
58164 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes);
58165 if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) {
58166 return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
58167 }
58168
58169 /* Move the read pointer back to the start if necessary. */
58170 newWriteOffsetLoopFlag = writeOffsetLoopFlag;
58171 if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) {
58172 newWriteOffsetInBytes = 0;
58173 newWriteOffsetLoopFlag ^= 0x80000000;
58174 }
58175
58176 ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag));
58177
58178 return MA_SUCCESS;
58179}
58180
58181MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes)
58182{
58183 ma_uint32 readOffset;
58184 ma_uint32 readOffsetInBytes;
58185 ma_uint32 readOffsetLoopFlag;
58186 ma_uint32 writeOffset;
58187 ma_uint32 writeOffsetInBytes;
58188 ma_uint32 writeOffsetLoopFlag;
58189 ma_uint32 newReadOffsetInBytes;
58190 ma_uint32 newReadOffsetLoopFlag;
58191
58192 if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) {
58193 return MA_INVALID_ARGS;
58194 }
58195
58196 readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
58197 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
58198
58199 writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
58200 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
58201
58202 newReadOffsetLoopFlag = readOffsetLoopFlag;
58203
58204 /* We cannot go past the write buffer. */
58205 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
58206 if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) {
58207 newReadOffsetInBytes = writeOffsetInBytes;
58208 } else {
58209 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
58210 }
58211 } else {
58212 /* May end up looping. */
58213 if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
58214 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
58215 newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
58216 } else {
58217 newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
58218 }
58219 }
58220
58221 ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag));
58222 return MA_SUCCESS;
58223}
58224
58225MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes)
58226{
58227 ma_uint32 readOffset;
58228 ma_uint32 readOffsetInBytes;
58229 ma_uint32 readOffsetLoopFlag;
58230 ma_uint32 writeOffset;
58231 ma_uint32 writeOffsetInBytes;
58232 ma_uint32 writeOffsetLoopFlag;
58233 ma_uint32 newWriteOffsetInBytes;
58234 ma_uint32 newWriteOffsetLoopFlag;
58235
58236 if (pRB == NULL) {
58237 return MA_INVALID_ARGS;
58238 }
58239
58240 readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
58241 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
58242
58243 writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
58244 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
58245
58246 newWriteOffsetLoopFlag = writeOffsetLoopFlag;
58247
58248 /* We cannot go past the write buffer. */
58249 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
58250 /* May end up looping. */
58251 if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
58252 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
58253 newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
58254 } else {
58255 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
58256 }
58257 } else {
58258 if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) {
58259 newWriteOffsetInBytes = readOffsetInBytes;
58260 } else {
58261 newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
58262 }
58263 }
58264
58265 ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag));
58266 return MA_SUCCESS;
58267}
58268
58270{
58271 ma_uint32 readOffset;
58272 ma_uint32 readOffsetInBytes;
58273 ma_uint32 readOffsetLoopFlag;
58274 ma_uint32 writeOffset;
58275 ma_uint32 writeOffsetInBytes;
58276 ma_uint32 writeOffsetLoopFlag;
58277
58278 if (pRB == NULL) {
58279 return 0;
58280 }
58281
58282 readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
58283 ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
58284
58285 writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
58286 ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
58287
58288 if (readOffsetLoopFlag == writeOffsetLoopFlag) {
58289 return writeOffsetInBytes - readOffsetInBytes;
58290 } else {
58291 return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes);
58292 }
58293}
58294
58296{
58297 ma_int32 dist;
58298
58299 if (pRB == NULL) {
58300 return 0;
58301 }
58302
58303 dist = ma_rb_pointer_distance(pRB);
58304 if (dist < 0) {
58305 return 0;
58306 }
58307
58308 return dist;
58309}
58310
58312{
58313 if (pRB == NULL) {
58314 return 0;
58315 }
58316
58318}
58319
58320MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB)
58321{
58322 if (pRB == NULL) {
58323 return 0;
58324 }
58325
58326 return pRB->subbufferSizeInBytes;
58327}
58328
58329MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB)
58330{
58331 if (pRB == NULL) {
58332 return 0;
58333 }
58334
58335 if (pRB->subbufferStrideInBytes == 0) {
58336 return (size_t)pRB->subbufferSizeInBytes;
58337 }
58338
58339 return (size_t)pRB->subbufferStrideInBytes;
58340}
58341
58342MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex)
58343{
58344 if (pRB == NULL) {
58345 return 0;
58346 }
58347
58348 return subbufferIndex * ma_rb_get_subbuffer_stride(pRB);
58349}
58350
58351MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer)
58352{
58353 if (pRB == NULL) {
58354 return NULL;
58355 }
58356
58357 return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex));
58358}
58359
58360
58361
58362static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
58363{
58364 /* 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. */
58365 ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource;
58366 ma_result result;
58367 ma_uint64 totalFramesRead;
58368
58369 MA_ASSERT(pRB != NULL);
58370
58371 /* We need to run this in a loop since the ring buffer itself may loop. */
58372 totalFramesRead = 0;
58373 while (totalFramesRead < frameCount) {
58374 void* pMappedBuffer;
58375 ma_uint32 mappedFrameCount;
58376 ma_uint64 framesToRead = frameCount - totalFramesRead;
58377 if (framesToRead > 0xFFFFFFFF) {
58378 framesToRead = 0xFFFFFFFF;
58379 }
58380
58381 mappedFrameCount = (ma_uint32)framesToRead;
58382 result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer);
58383 if (result != MA_SUCCESS) {
58384 break;
58385 }
58386
58387 if (mappedFrameCount == 0) {
58388 break; /* <-- End of ring buffer. */
58389 }
58390
58391 ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), pMappedBuffer, mappedFrameCount, pRB->format, pRB->channels);
58392
58393 result = ma_pcm_rb_commit_read(pRB, mappedFrameCount);
58394 if (result != MA_SUCCESS) {
58395 break;
58396 }
58397
58398 totalFramesRead += mappedFrameCount;
58399 }
58400
58401 /*
58402 There is no notion of an "end" in a ring buffer. If we didn't have enough data to fill the requested frame
58403 count we'll need to pad with silence. If we don't do this, totalFramesRead might equal 0 which will result
58404 in the data source layer at a higher level translating this to MA_AT_END which is incorrect for a ring buffer.
58405 */
58406 if (totalFramesRead < frameCount) {
58407 ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), (frameCount - totalFramesRead), pRB->format, pRB->channels);
58408 totalFramesRead = frameCount;
58409 }
58410
58411 *pFramesRead = totalFramesRead;
58412 return MA_SUCCESS;
58413}
58414
58415static 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)
58416{
58417 ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource;
58418 MA_ASSERT(pRB != NULL);
58419
58420 if (pFormat != NULL) {
58421 *pFormat = pRB->format;
58422 }
58423
58424 if (pChannels != NULL) {
58425 *pChannels = pRB->channels;
58426 }
58427
58428 if (pSampleRate != NULL) {
58429 *pSampleRate = pRB->sampleRate;
58430 }
58431
58432 /* Just assume the default channel map. */
58433 if (pChannelMap != NULL) {
58435 }
58436
58437 return MA_SUCCESS;
58438}
58439
58440static ma_data_source_vtable ma_gRBDataSourceVTable =
58441{
58442 ma_pcm_rb_data_source__on_read,
58443 NULL, /* onSeek */
58444 ma_pcm_rb_data_source__on_get_data_format,
58445 NULL, /* onGetCursor */
58446 NULL, /* onGetLength */
58447 NULL, /* onSetLooping */
58448 0
58449};
58450
58451static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB)
58452{
58453 MA_ASSERT(pRB != NULL);
58454
58455 return ma_get_bytes_per_frame(pRB->format, pRB->channels);
58456}
58457
58458MA_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)
58459{
58460 ma_uint32 bpf;
58461 ma_result result;
58462
58463 if (pRB == NULL) {
58464 return MA_INVALID_ARGS;
58465 }
58466
58467 MA_ZERO_OBJECT(pRB);
58468
58469 bpf = ma_get_bytes_per_frame(format, channels);
58470 if (bpf == 0) {
58471 return MA_INVALID_ARGS;
58472 }
58473
58474 result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb);
58475 if (result != MA_SUCCESS) {
58476 return result;
58477 }
58478
58479 pRB->format = format;
58480 pRB->channels = channels;
58481 pRB->sampleRate = 0; /* The sample rate is not passed in as a parameter. */
58482
58483 /* The PCM ring buffer is a data source. We need to get that set up as well. */
58484 {
58485 ma_data_source_config dataSourceConfig = ma_data_source_config_init();
58486 dataSourceConfig.vtable = &ma_gRBDataSourceVTable;
58487
58488 result = ma_data_source_init(&dataSourceConfig, &pRB->ds);
58489 if (result != MA_SUCCESS) {
58490 ma_rb_uninit(&pRB->rb);
58491 return result;
58492 }
58493 }
58494
58495 return MA_SUCCESS;
58496}
58497
58498MA_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)
58499{
58500 return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
58501}
58502
58503MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB)
58504{
58505 if (pRB == NULL) {
58506 return;
58507 }
58508
58510 ma_rb_uninit(&pRB->rb);
58511}
58512
58513MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB)
58514{
58515 if (pRB == NULL) {
58516 return;
58517 }
58518
58519 ma_rb_reset(&pRB->rb);
58520}
58521
58522MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
58523{
58524 size_t sizeInBytes;
58525 ma_result result;
58526
58527 if (pRB == NULL || pSizeInFrames == NULL) {
58528 return MA_INVALID_ARGS;
58529 }
58530
58531 sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
58532
58533 result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut);
58534 if (result != MA_SUCCESS) {
58535 return result;
58536 }
58537
58538 *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB));
58539 return MA_SUCCESS;
58540}
58541
58542MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames)
58543{
58544 if (pRB == NULL) {
58545 return MA_INVALID_ARGS;
58546 }
58547
58548 return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB));
58549}
58550
58551MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
58552{
58553 size_t sizeInBytes;
58554 ma_result result;
58555
58556 if (pRB == NULL) {
58557 return MA_INVALID_ARGS;
58558 }
58559
58560 sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
58561
58562 result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut);
58563 if (result != MA_SUCCESS) {
58564 return result;
58565 }
58566
58567 *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB));
58568 return MA_SUCCESS;
58569}
58570
58571MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames)
58572{
58573 if (pRB == NULL) {
58574 return MA_INVALID_ARGS;
58575 }
58576
58577 return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB));
58578}
58579
58580MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
58581{
58582 if (pRB == NULL) {
58583 return MA_INVALID_ARGS;
58584 }
58585
58586 return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
58587}
58588
58589MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
58590{
58591 if (pRB == NULL) {
58592 return MA_INVALID_ARGS;
58593 }
58594
58595 return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
58596}
58597
58599{
58600 if (pRB == NULL) {
58601 return 0;
58602 }
58603
58604 return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
58605}
58606
58608{
58609 if (pRB == NULL) {
58610 return 0;
58611 }
58612
58613 return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
58614}
58615
58617{
58618 if (pRB == NULL) {
58619 return 0;
58620 }
58621
58622 return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
58623}
58624
58626{
58627 if (pRB == NULL) {
58628 return 0;
58629 }
58630
58631 return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
58632}
58633
58635{
58636 if (pRB == NULL) {
58637 return 0;
58638 }
58639
58640 return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
58641}
58642
58643MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex)
58644{
58645 if (pRB == NULL) {
58646 return 0;
58647 }
58648
58649 return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB));
58650}
58651
58652MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer)
58653{
58654 if (pRB == NULL) {
58655 return NULL;
58656 }
58657
58658 return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer);
58659}
58660
58661MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB)
58662{
58663 if (pRB == NULL) {
58664 return ma_format_unknown;
58665 }
58666
58667 return pRB->format;
58668}
58669
58670MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB)
58671{
58672 if (pRB == NULL) {
58673 return 0;
58674 }
58675
58676 return pRB->channels;
58677}
58678
58679MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB)
58680{
58681 if (pRB == NULL) {
58682 return 0;
58683 }
58684
58685 return pRB->sampleRate;
58686}
58687
58688MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate)
58689{
58690 if (pRB == NULL) {
58691 return;
58692 }
58693
58694 pRB->sampleRate = sampleRate;
58695}
58696
58697
58698
58699MA_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)
58700{
58701 ma_result result;
58702 ma_uint32 sizeInFrames;
58703
58704 sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5);
58705 if (sizeInFrames == 0) {
58706 return MA_INVALID_ARGS;
58707 }
58708
58709 result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb);
58710 if (result != MA_SUCCESS) {
58711 return result;
58712 }
58713
58714 /* Seek forward a bit so we have a bit of a buffer in case of desyncs. */
58715 ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2);
58716
58717 return MA_SUCCESS;
58718}
58719
58720MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB)
58721{
58722 ma_pcm_rb_uninit((ma_pcm_rb*)pRB);
58723 return MA_SUCCESS;
58724}
58725
58726
58727
58728/**************************************************************************************************************************************************************
58729
58730Miscellaneous Helpers
58731
58732**************************************************************************************************************************************************************/
58733MA_API const char* ma_result_description(ma_result result)
58734{
58735 switch (result)
58736 {
58737 case MA_SUCCESS: return "No error";
58738 case MA_ERROR: return "Unknown error";
58739 case MA_INVALID_ARGS: return "Invalid argument";
58740 case MA_INVALID_OPERATION: return "Invalid operation";
58741 case MA_OUT_OF_MEMORY: return "Out of memory";
58742 case MA_OUT_OF_RANGE: return "Out of range";
58743 case MA_ACCESS_DENIED: return "Permission denied";
58744 case MA_DOES_NOT_EXIST: return "Resource does not exist";
58745 case MA_ALREADY_EXISTS: return "Resource already exists";
58746 case MA_TOO_MANY_OPEN_FILES: return "Too many open files";
58747 case MA_INVALID_FILE: return "Invalid file";
58748 case MA_TOO_BIG: return "Too large";
58749 case MA_PATH_TOO_LONG: return "Path too long";
58750 case MA_NAME_TOO_LONG: return "Name too long";
58751 case MA_NOT_DIRECTORY: return "Not a directory";
58752 case MA_IS_DIRECTORY: return "Is a directory";
58753 case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty";
58754 case MA_AT_END: return "At end";
58755 case MA_NO_SPACE: return "No space available";
58756 case MA_BUSY: return "Device or resource busy";
58757 case MA_IO_ERROR: return "Input/output error";
58758 case MA_INTERRUPT: return "Interrupted";
58759 case MA_UNAVAILABLE: return "Resource unavailable";
58760 case MA_ALREADY_IN_USE: return "Resource already in use";
58761 case MA_BAD_ADDRESS: return "Bad address";
58762 case MA_BAD_SEEK: return "Illegal seek";
58763 case MA_BAD_PIPE: return "Broken pipe";
58764 case MA_DEADLOCK: return "Deadlock";
58765 case MA_TOO_MANY_LINKS: return "Too many links";
58766 case MA_NOT_IMPLEMENTED: return "Not implemented";
58767 case MA_NO_MESSAGE: return "No message of desired type";
58768 case MA_BAD_MESSAGE: return "Invalid message";
58769 case MA_NO_DATA_AVAILABLE: return "No data available";
58770 case MA_INVALID_DATA: return "Invalid data";
58771 case MA_TIMEOUT: return "Timeout";
58772 case MA_NO_NETWORK: return "Network unavailable";
58773 case MA_NOT_UNIQUE: return "Not unique";
58774 case MA_NOT_SOCKET: return "Socket operation on non-socket";
58775 case MA_NO_ADDRESS: return "Destination address required";
58776 case MA_BAD_PROTOCOL: return "Protocol wrong type for socket";
58777 case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available";
58778 case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported";
58779 case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported";
58780 case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported";
58781 case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported";
58782 case MA_CONNECTION_RESET: return "Connection reset";
58783 case MA_ALREADY_CONNECTED: return "Already connected";
58784 case MA_NOT_CONNECTED: return "Not connected";
58785 case MA_CONNECTION_REFUSED: return "Connection refused";
58786 case MA_NO_HOST: return "No host";
58787 case MA_IN_PROGRESS: return "Operation in progress";
58788 case MA_CANCELLED: return "Operation cancelled";
58789 case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped";
58790
58791 case MA_FORMAT_NOT_SUPPORTED: return "Format not supported";
58792 case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported";
58793 case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported";
58794 case MA_NO_BACKEND: return "No backend";
58795 case MA_NO_DEVICE: return "No device";
58796 case MA_API_NOT_FOUND: return "API not found";
58797 case MA_INVALID_DEVICE_CONFIG: return "Invalid device config";
58798
58799 case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized";
58800 case MA_DEVICE_NOT_STARTED: return "Device not started";
58801
58802 case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend";
58803 case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device";
58804 case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device";
58805 case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device";
58806
58807 default: return "Unknown error";
58808 }
58809}
58810
58811MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
58812{
58813 if (pAllocationCallbacks != NULL) {
58814 if (pAllocationCallbacks->onMalloc != NULL) {
58815 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
58816 } else {
58817 return NULL; /* Do not fall back to the default implementation. */
58818 }
58819 } else {
58820 return ma__malloc_default(sz, NULL);
58821 }
58822}
58823
58824MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
58825{
58826 void* p = ma_malloc(sz, pAllocationCallbacks);
58827 if (p != NULL) {
58828 MA_ZERO_MEMORY(p, sz);
58829 }
58830
58831 return p;
58832}
58833
58834MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
58835{
58836 if (pAllocationCallbacks != NULL) {
58837 if (pAllocationCallbacks->onRealloc != NULL) {
58838 return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData);
58839 } else {
58840 return NULL; /* Do not fall back to the default implementation. */
58841 }
58842 } else {
58843 return ma__realloc_default(p, sz, NULL);
58844 }
58845}
58846
58847MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
58848{
58849 if (p == NULL) {
58850 return;
58851 }
58852
58853 if (pAllocationCallbacks != NULL) {
58854 if (pAllocationCallbacks->onFree != NULL) {
58855 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
58856 } else {
58857 return; /* Do no fall back to the default implementation. */
58858 }
58859 } else {
58860 ma__free_default(p, NULL);
58861 }
58862}
58863
58864MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks)
58865{
58866 size_t extraBytes;
58867 void* pUnaligned;
58868 void* pAligned;
58869
58870 if (alignment == 0) {
58871 return 0;
58872 }
58873
58874 extraBytes = alignment-1 + sizeof(void*);
58875
58876 pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks);
58877 if (pUnaligned == NULL) {
58878 return NULL;
58879 }
58880
58881 pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1)));
58882 ((void**)pAligned)[-1] = pUnaligned;
58883
58884 return pAligned;
58885}
58886
58887MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
58888{
58889 ma_free(((void**)p)[-1], pAllocationCallbacks);
58890}
58891
58892MA_API const char* ma_get_format_name(ma_format format)
58893{
58894 switch (format)
58895 {
58896 case ma_format_unknown: return "Unknown";
58897 case ma_format_u8: return "8-bit Unsigned Integer";
58898 case ma_format_s16: return "16-bit Signed Integer";
58899 case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)";
58900 case ma_format_s32: return "32-bit Signed Integer";
58901 case ma_format_f32: return "32-bit IEEE Floating Point";
58902 default: return "Invalid";
58903 }
58904}
58905
58906MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels)
58907{
58908 ma_uint32 i;
58909 for (i = 0; i < channels; ++i) {
58910 pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor);
58911 }
58912}
58913
58914
58916{
58917 ma_uint32 sizes[] = {
58918 0, /* unknown */
58919 1, /* u8 */
58920 2, /* s16 */
58921 3, /* s24 */
58922 4, /* s32 */
58923 4, /* f32 */
58924 };
58925 return sizes[format];
58926}
58927
58928
58929
58930#define MA_DATA_SOURCE_DEFAULT_RANGE_BEG 0
58931#define MA_DATA_SOURCE_DEFAULT_RANGE_END ~((ma_uint64)0)
58932#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG 0
58933#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END ~((ma_uint64)0)
58934
58935MA_API ma_data_source_config ma_data_source_config_init(void)
58936{
58937 ma_data_source_config config;
58938
58939 MA_ZERO_OBJECT(&config);
58940
58941 return config;
58942}
58943
58944
58945MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource)
58946{
58947 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
58948
58949 if (pDataSource == NULL) {
58950 return MA_INVALID_ARGS;
58951 }
58952
58953 MA_ZERO_OBJECT(pDataSourceBase);
58954
58955 if (pConfig == NULL) {
58956 return MA_INVALID_ARGS;
58957 }
58958
58959 if (pConfig->vtable == NULL) {
58960 return MA_INVALID_ARGS;
58961 }
58962
58963 pDataSourceBase->vtable = pConfig->vtable;
58964 pDataSourceBase->rangeBegInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG;
58965 pDataSourceBase->rangeEndInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END;
58966 pDataSourceBase->loopBegInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG;
58967 pDataSourceBase->loopEndInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END;
58968 pDataSourceBase->pCurrent = pDataSource; /* Always read from ourself by default. */
58969 pDataSourceBase->pNext = NULL;
58970 pDataSourceBase->onGetNext = NULL;
58971
58972 return MA_SUCCESS;
58973}
58974
58976{
58977 if (pDataSource == NULL) {
58978 return;
58979 }
58980
58981 /*
58982 This is placeholder in case we need this later. Data sources need to call this in their
58983 uninitialization routine to ensure things work later on if something is added here.
58984 */
58985}
58986
58987static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource)
58988{
58989 ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource;
58990
58991 MA_ASSERT(pDataSource != NULL);
58992 MA_ASSERT(ppCurrentDataSource != NULL);
58993
58994 if (pCurrentDataSource->pCurrent == NULL) {
58995 /*
58996 The current data source is NULL. If we're using this in the context of a chain we need to return NULL
58997 here so that we don't end up looping. Otherwise we just return the data source itself.
58998 */
58999 if (pCurrentDataSource->pNext != NULL || pCurrentDataSource->onGetNext != NULL) {
59000 pCurrentDataSource = NULL;
59001 } else {
59002 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. */
59003 }
59004 } else {
59005 pCurrentDataSource = (ma_data_source_base*)pCurrentDataSource->pCurrent;
59006 }
59007
59008 *ppCurrentDataSource = pCurrentDataSource;
59009
59010 return MA_SUCCESS;
59011}
59012
59013static ma_result ma_data_source_read_pcm_frames_from_backend(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
59014{
59015 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
59016
59017 MA_ASSERT(pDataSourceBase != NULL);
59018 MA_ASSERT(pDataSourceBase->vtable != NULL);
59019 MA_ASSERT(pDataSourceBase->vtable->onRead != NULL);
59020 MA_ASSERT(pFramesRead != NULL);
59021
59022 if (pFramesOut != NULL) {
59023 return pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, pFramesRead);
59024 } else {
59025 /*
59026 No output buffer. Probably seeking forward. Read and discard. Can probably optimize this in terms of
59027 onSeek and onGetCursor, but need to keep in mind that the data source may not implement these functions.
59028 */
59029 ma_result result;
59030 ma_uint64 framesRead;
59031 ma_format format;
59032 ma_uint32 channels;
59033 ma_uint64 discardBufferCapInFrames;
59034 ma_uint8 pDiscardBuffer[4096];
59035
59036 result = ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0);
59037 if (result != MA_SUCCESS) {
59038 return result;
59039 }
59040
59041 discardBufferCapInFrames = sizeof(pDiscardBuffer) / ma_get_bytes_per_frame(format, channels);
59042
59043 framesRead = 0;
59044 while (framesRead < frameCount) {
59045 ma_uint64 framesReadThisIteration = 0;
59046 ma_uint64 framesToRead = frameCount - framesRead;
59047 if (framesToRead > discardBufferCapInFrames) {
59048 framesToRead = discardBufferCapInFrames;
59049 }
59050
59051 result = pDataSourceBase->vtable->onRead(pDataSourceBase, pDiscardBuffer, framesToRead, &framesReadThisIteration);
59052 if (result != MA_SUCCESS) {
59053 return result;
59054 }
59055
59056 framesRead += framesReadThisIteration;
59057 }
59058
59059 *pFramesRead = framesRead;
59060
59061 return MA_SUCCESS;
59062 }
59063}
59064
59065static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
59066{
59067 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
59068 ma_result result;
59069 ma_uint64 framesRead = 0;
59070 ma_bool32 loop = ma_data_source_is_looping(pDataSource);
59071
59072 if (pDataSourceBase == NULL) {
59073 return MA_AT_END;
59074 }
59075
59076 if (frameCount == 0) {
59077 return MA_INVALID_ARGS;
59078 }
59079
59080 MA_ASSERT(pDataSourceBase->vtable != NULL);
59081
59082 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))) {
59083 /* 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. */
59084 result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead);
59085 } else {
59086 /* Need to clamp to within the range. */
59087 ma_uint64 relativeCursor;
59088 ma_uint64 absoluteCursor;
59089
59090 result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &relativeCursor);
59091 if (result != MA_SUCCESS) {
59092 /* 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. */
59093 result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead);
59094 } else {
59095 ma_uint64 rangeBeg;
59096 ma_uint64 rangeEnd;
59097
59098 /* We have the cursor. We need to make sure we don't read beyond our range. */
59099 rangeBeg = pDataSourceBase->rangeBegInFrames;
59100 rangeEnd = pDataSourceBase->rangeEndInFrames;
59101
59102 absoluteCursor = rangeBeg + relativeCursor;
59103
59104 /* If looping, make sure we're within range. */
59105 if (loop) {
59106 if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) {
59107 rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames);
59108 }
59109 }
59110
59111 if (frameCount > (rangeEnd - absoluteCursor) && rangeEnd != ~((ma_uint64)0)) {
59112 frameCount = (rangeEnd - absoluteCursor);
59113 }
59114
59115 /*
59116 If the cursor is sitting on the end of the range the frame count will be set to 0 which can
59117 result in MA_INVALID_ARGS. In this case, we don't want to try reading, but instead return
59118 MA_AT_END so the higher level function can know about it.
59119 */
59120 if (frameCount > 0) {
59121 result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead);
59122 } else {
59123 result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */
59124 }
59125 }
59126 }
59127
59128 if (pFramesRead != NULL) {
59129 *pFramesRead = framesRead;
59130 }
59131
59132 /* We need to make sure MA_AT_END is returned if we hit the end of the range. */
59133 if (result == MA_SUCCESS && framesRead == 0) {
59134 result = MA_AT_END;
59135 }
59136
59137 return result;
59138}
59139
59140MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
59141{
59142 ma_result result = MA_SUCCESS;
59143 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
59144 ma_data_source_base* pCurrentDataSource;
59145 void* pRunningFramesOut = pFramesOut;
59146 ma_uint64 totalFramesProcessed = 0;
59147 ma_format format;
59148 ma_uint32 channels;
59149 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. */
59150 ma_bool32 loop;
59151
59152 if (pFramesRead != NULL) {
59153 *pFramesRead = 0;
59154 }
59155
59156 if (frameCount == 0) {
59157 return MA_INVALID_ARGS;
59158 }
59159
59160 if (pDataSourceBase == NULL) {
59161 return MA_INVALID_ARGS;
59162 }
59163
59164 loop = ma_data_source_is_looping(pDataSource);
59165
59166 /*
59167 We need to know the data format so we can advance the output buffer as we read frames. If this
59168 fails, chaining will not work and we'll just read as much as we can from the current source.
59169 */
59170 if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0) != MA_SUCCESS) {
59171 result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource);
59172 if (result != MA_SUCCESS) {
59173 return result;
59174 }
59175
59176 return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead);
59177 }
59178
59179 /*
59180 Looping is a bit of a special case. When the `loop` argument is true, chaining will not work and
59181 only the current data source will be read from.
59182 */
59183
59184 /* Keep reading until we've read as many frames as possible. */
59185 while (totalFramesProcessed < frameCount) {
59186 ma_uint64 framesProcessed;
59187 ma_uint64 framesRemaining = frameCount - totalFramesProcessed;
59188
59189 /* We need to resolve the data source that we'll actually be reading from. */
59190 result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource);
59191 if (result != MA_SUCCESS) {
59192 break;
59193 }
59194
59195 if (pCurrentDataSource == NULL) {
59196 break;
59197 }
59198
59199 result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed);
59200 totalFramesProcessed += framesProcessed;
59201
59202 /*
59203 If we encountered 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
59204 not necessarily considered an error.
59205 */
59206 if (result != MA_SUCCESS && result != MA_AT_END) {
59207 break;
59208 }
59209
59210 /*
59211 We can determine if we've reached the end by checking if ma_data_source_read_pcm_frames_within_range() returned
59212 MA_AT_END. To loop back to the start, all we need to do is seek back to the first frame.
59213 */
59214 if (result == MA_AT_END) {
59215 /*
59216 The result needs to be reset back to MA_SUCCESS (from MA_AT_END) so that we don't
59217 accidentally return MA_AT_END when data has been read in prior loop iterations. at the
59218 end of this function, the result will be checked for MA_SUCCESS, and if the total
59219 number of frames processed is 0, will be explicitly set to MA_AT_END.
59220 */
59221 result = MA_SUCCESS;
59222
59223 /*
59224 We reached the end. If we're looping, we just loop back to the start of the current
59225 data source. If we're not looping we need to check if we have another in the chain, and
59226 if so, switch to it.
59227 */
59228 if (loop) {
59229 if (framesProcessed == 0) {
59230 emptyLoopCounter += 1;
59231 if (emptyLoopCounter > 1) {
59232 break; /* Infinite loop detected. Get out. */
59233 }
59234 } else {
59235 emptyLoopCounter = 0;
59236 }
59237
59238 result = ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames);
59239 if (result != MA_SUCCESS) {
59240 break; /* Failed to loop. Abort. */
59241 }
59242
59243 /* Don't return MA_AT_END for looping sounds. */
59244 result = MA_SUCCESS;
59245 } else {
59246 if (pCurrentDataSource->pNext != NULL) {
59247 pDataSourceBase->pCurrent = pCurrentDataSource->pNext;
59248 } else if (pCurrentDataSource->onGetNext != NULL) {
59249 pDataSourceBase->pCurrent = pCurrentDataSource->onGetNext(pCurrentDataSource);
59250 if (pDataSourceBase->pCurrent == NULL) {
59251 break; /* Our callback did not return a next data source. We're done. */
59252 }
59253 } else {
59254 /* Reached the end of the chain. We're done. */
59255 break;
59256 }
59257
59258 /* The next data source needs to be rewound to ensure data is read in looping scenarios. */
59259 result = ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0);
59260 if (result != MA_SUCCESS) {
59261 break;
59262 }
59263 }
59264 }
59265
59266 if (pRunningFramesOut != NULL) {
59267 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels));
59268 }
59269 }
59270
59271 if (pFramesRead != NULL) {
59272 *pFramesRead = totalFramesProcessed;
59273 }
59274
59275 MA_ASSERT(!(result == MA_AT_END && totalFramesProcessed > 0)); /* We should never be returning MA_AT_END if we read some data. */
59276
59277 if (result == MA_SUCCESS && totalFramesProcessed == 0) {
59278 result = MA_AT_END;
59279 }
59280
59281 return result;
59282}
59283
59284MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked)
59285{
59286 return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked);
59287}
59288
59290{
59291 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
59292
59293 if (pDataSourceBase == NULL) {
59294 return MA_INVALID_ARGS;
59295 }
59296
59297 if (pDataSourceBase->vtable->onSeek == NULL) {
59298 return MA_NOT_IMPLEMENTED;
59299 }
59300
59301 if (frameIndex > pDataSourceBase->rangeEndInFrames) {
59302 return MA_INVALID_OPERATION; /* Trying to seek too far forward. */
59303 }
59304
59305 MA_ASSERT(pDataSourceBase->vtable != NULL);
59306
59307 return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex);
59308}
59309
59310MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked)
59311{
59312 ma_uint64 frameCount;
59313 ma_uint64 framesSeeked = 0;
59314 ma_uint32 sampleRate;
59315 ma_result result;
59316
59317 if (pDataSource == NULL) {
59318 return MA_INVALID_ARGS;
59319 }
59320
59321 result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
59322 if (result != MA_SUCCESS) {
59323 return result;
59324 }
59325
59326 /* We need PCM frames instead of seconds */
59327 frameCount = (ma_uint64)(secondCount * sampleRate);
59328
59329 result = ma_data_source_seek_pcm_frames(pDataSource, frameCount, &framesSeeked);
59330
59331 /* VC6 doesn't support division between unsigned 64-bit integer and floating point number. Signed integer needed. This shouldn't affect anything in practice */
59332 *pSecondsSeeked = (ma_int64)framesSeeked / (float)sampleRate;
59333 return result;
59334}
59335
59336MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds)
59337{
59338 ma_uint64 frameIndex;
59339 ma_uint32 sampleRate;
59340 ma_result result;
59341
59342 if (pDataSource == NULL) {
59343 return MA_INVALID_ARGS;
59344 }
59345
59346 result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
59347 if (result != MA_SUCCESS) {
59348 return result;
59349 }
59350
59351 /* We need PCM frames instead of seconds */
59352 frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate);
59353
59354 return ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex);
59355}
59356
59357MA_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)
59358{
59359 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
59360 ma_result result;
59361 ma_format format;
59362 ma_uint32 channels;
59363 ma_uint32 sampleRate;
59364
59365 /* Initialize to defaults for safety just in case the data source does not implement this callback. */
59366 if (pFormat != NULL) {
59367 *pFormat = ma_format_unknown;
59368 }
59369 if (pChannels != NULL) {
59370 *pChannels = 0;
59371 }
59372 if (pSampleRate != NULL) {
59373 *pSampleRate = 0;
59374 }
59375 if (pChannelMap != NULL) {
59376 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
59377 }
59378
59379 if (pDataSourceBase == NULL) {
59380 return MA_INVALID_ARGS;
59381 }
59382
59383 MA_ASSERT(pDataSourceBase->vtable != NULL);
59384
59385 if (pDataSourceBase->vtable->onGetDataFormat == NULL) {
59386 return MA_NOT_IMPLEMENTED;
59387 }
59388
59389 result = pDataSourceBase->vtable->onGetDataFormat(pDataSource, &format, &channels, &sampleRate, pChannelMap, channelMapCap);
59390 if (result != MA_SUCCESS) {
59391 return result;
59392 }
59393
59394 if (pFormat != NULL) {
59395 *pFormat = format;
59396 }
59397 if (pChannels != NULL) {
59398 *pChannels = channels;
59399 }
59400 if (pSampleRate != NULL) {
59401 *pSampleRate = sampleRate;
59402 }
59403
59404 /* Channel map was passed in directly to the callback. This is safe due to the channelMapCap parameter. */
59405
59406 return MA_SUCCESS;
59407}
59408
59410{
59411 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
59412 ma_result result;
59413 ma_uint64 cursor;
59414
59415 if (pCursor == NULL) {
59416 return MA_INVALID_ARGS;
59417 }
59418
59419 *pCursor = 0;
59420
59421 if (pDataSourceBase == NULL) {
59422 return MA_SUCCESS;
59423 }
59424
59425 MA_ASSERT(pDataSourceBase->vtable != NULL);
59426
59427 if (pDataSourceBase->vtable->onGetCursor == NULL) {
59428 return MA_NOT_IMPLEMENTED;
59429 }
59430
59431 result = pDataSourceBase->vtable->onGetCursor(pDataSourceBase, &cursor);
59432 if (result != MA_SUCCESS) {
59433 return result;
59434 }
59435
59436 /* The cursor needs to be made relative to the start of the range. */
59437 if (cursor < pDataSourceBase->rangeBegInFrames) { /* Safety check so we don't return some huge number. */
59438 *pCursor = 0;
59439 } else {
59440 *pCursor = cursor - pDataSourceBase->rangeBegInFrames;
59441 }
59442
59443 return MA_SUCCESS;
59444}
59445
59447{
59448 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
59449
59450 if (pLength == NULL) {
59451 return MA_INVALID_ARGS;
59452 }
59453
59454 *pLength = 0;
59455
59456 if (pDataSourceBase == NULL) {
59457 return MA_INVALID_ARGS;
59458 }
59459
59460 MA_ASSERT(pDataSourceBase->vtable != NULL);
59461
59462 /*
59463 If we have a range defined we'll use that to determine the length. This is one of rare times
59464 where we'll actually trust the caller. If they've set the range, I think it's mostly safe to
59465 assume they've set it based on some higher level knowledge of the structure of the sound bank.
59466 */
59467 if (pDataSourceBase->rangeEndInFrames != ~((ma_uint64)0)) {
59468 *pLength = pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames;
59469 return MA_SUCCESS;
59470 }
59471
59472 /*
59473 Getting here means a range is not defined so we'll need to get the data source itself to tell
59474 us the length.
59475 */
59476 if (pDataSourceBase->vtable->onGetLength == NULL) {
59477 return MA_NOT_IMPLEMENTED;
59478 }
59479
59480 return pDataSourceBase->vtable->onGetLength(pDataSource, pLength);
59481}
59482
59484{
59485 ma_result result;
59486 ma_uint64 cursorInPCMFrames;
59487 ma_uint32 sampleRate;
59488
59489 if (pCursor == NULL) {
59490 return MA_INVALID_ARGS;
59491 }
59492
59493 *pCursor = 0;
59494
59495 result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursorInPCMFrames);
59496 if (result != MA_SUCCESS) {
59497 return result;
59498 }
59499
59500 result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
59501 if (result != MA_SUCCESS) {
59502 return result;
59503 }
59504
59505 /* 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. */
59506 *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate;
59507
59508 return MA_SUCCESS;
59509}
59510
59512{
59513 ma_result result;
59514 ma_uint64 lengthInPCMFrames;
59515 ma_uint32 sampleRate;
59516
59517 if (pLength == NULL) {
59518 return MA_INVALID_ARGS;
59519 }
59520
59521 *pLength = 0;
59522
59523 result = ma_data_source_get_length_in_pcm_frames(pDataSource, &lengthInPCMFrames);
59524 if (result != MA_SUCCESS) {
59525 return result;
59526 }
59527
59528 result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
59529 if (result != MA_SUCCESS) {
59530 return result;
59531 }
59532
59533 /* 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. */
59534 *pLength = (ma_int64)lengthInPCMFrames / (float)sampleRate;
59535
59536 return MA_SUCCESS;
59537}
59538
59540{
59541 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
59542
59543 if (pDataSource == NULL) {
59544 return MA_INVALID_ARGS;
59545 }
59546
59547 ma_atomic_exchange_32(&pDataSourceBase->isLooping, isLooping);
59548
59549 MA_ASSERT(pDataSourceBase->vtable != NULL);
59550
59551 /* If there's no callback for this just treat it as a successful no-op. */
59552 if (pDataSourceBase->vtable->onSetLooping == NULL) {
59553 return MA_SUCCESS;
59554 }
59555
59556 return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping);
59557}
59558
59560{
59561 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
59562
59563 if (pDataSource == NULL) {
59564 return MA_FALSE;
59565 }
59566
59567 return ma_atomic_load_32(&pDataSourceBase->isLooping);
59568}
59569
59570MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames)
59571{
59572 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
59573 ma_result result;
59574 ma_uint64 relativeCursor;
59575 ma_uint64 absoluteCursor;
59576 ma_bool32 doSeekAdjustment = MA_FALSE;
59577
59578 if (pDataSource == NULL) {
59579 return MA_INVALID_ARGS;
59580 }
59581
59582 if (rangeEndInFrames < rangeBegInFrames) {
59583 return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */
59584 }
59585
59586 /*
59587 We may need to adjust the position of the cursor to ensure it's clamped to the range. Grab it now
59588 so we can calculate its absolute position before we change the range.
59589 */
59590 result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &relativeCursor);
59591 if (result == MA_SUCCESS) {
59592 doSeekAdjustment = MA_TRUE;
59593 absoluteCursor = relativeCursor + pDataSourceBase->rangeBegInFrames;
59594 } else {
59595 /*
59596 We couldn't get the position of the cursor. It probably means the data source has no notion
59597 of a cursor. We'll just leave it at position 0. Don't treat this as an error.
59598 */
59599 doSeekAdjustment = MA_FALSE;
59600 relativeCursor = 0;
59601 absoluteCursor = 0;
59602 }
59603
59604 pDataSourceBase->rangeBegInFrames = rangeBegInFrames;
59605 pDataSourceBase->rangeEndInFrames = rangeEndInFrames;
59606
59607 /*
59608 The commented out logic below was intended to maintain loop points in response to a change in the
59609 range. However, this is not useful because it results in the sound breaking when you move the range
59610 outside of the old loop points. I'm simplifying this by simply resetting the loop points. The
59611 caller is expected to update their loop points if they change the range.
59612
59613 In practice this should be mostly a non-issue because the majority of the time the range will be
59614 set once right after initialization.
59615 */
59616 pDataSourceBase->loopBegInFrames = 0;
59617 pDataSourceBase->loopEndInFrames = ~((ma_uint64)0);
59618
59619
59620 /*
59621 Seek to within range. Note that our seek positions here are relative to the new range. We don't want
59622 to do this if we failed to retrieve the cursor earlier on because it probably means the data source
59623 has no notion of a cursor. In practice the seek would probably fail (which we silently ignore), but
59624 I'm just not even going to attempt it.
59625 */
59626 if (doSeekAdjustment) {
59627 if (absoluteCursor < rangeBegInFrames) {
59628 ma_data_source_seek_to_pcm_frame(pDataSource, 0);
59629 } else if (absoluteCursor > rangeEndInFrames) {
59630 ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames);
59631 }
59632 }
59633
59634 return MA_SUCCESS;
59635}
59636
59637MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames)
59638{
59639 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
59640
59641 if (pRangeBegInFrames != NULL) {
59642 *pRangeBegInFrames = 0;
59643 }
59644 if (pRangeEndInFrames != NULL) {
59645 *pRangeEndInFrames = 0;
59646 }
59647
59648 if (pDataSource == NULL) {
59649 return;
59650 }
59651
59652 if (pRangeBegInFrames != NULL) {
59653 *pRangeBegInFrames = pDataSourceBase->rangeBegInFrames;
59654 }
59655
59656 if (pRangeEndInFrames != NULL) {
59657 *pRangeEndInFrames = pDataSourceBase->rangeEndInFrames;
59658 }
59659}
59660
59662{
59663 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
59664
59665 if (pDataSource == NULL) {
59666 return MA_INVALID_ARGS;
59667 }
59668
59669 if (loopEndInFrames < loopBegInFrames) {
59670 return MA_INVALID_ARGS; /* The end of the loop point must come after the beginning. */
59671 }
59672
59673 if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) {
59674 return MA_INVALID_ARGS; /* The end of the loop point must not go beyond the range. */
59675 }
59676
59677 pDataSourceBase->loopBegInFrames = loopBegInFrames;
59678 pDataSourceBase->loopEndInFrames = loopEndInFrames;
59679
59680 /* The end cannot exceed the range. */
59681 if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) {
59682 pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames);
59683 }
59684
59685 return MA_SUCCESS;
59686}
59687
59688MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames)
59689{
59690 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
59691
59692 if (pLoopBegInFrames != NULL) {
59693 *pLoopBegInFrames = 0;
59694 }
59695 if (pLoopEndInFrames != NULL) {
59696 *pLoopEndInFrames = 0;
59697 }
59698
59699 if (pDataSource == NULL) {
59700 return;
59701 }
59702
59703 if (pLoopBegInFrames != NULL) {
59704 *pLoopBegInFrames = pDataSourceBase->loopBegInFrames;
59705 }
59706
59707 if (pLoopEndInFrames != NULL) {
59708 *pLoopEndInFrames = pDataSourceBase->loopEndInFrames;
59709 }
59710}
59711
59713{
59714 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
59715
59716 if (pDataSource == NULL) {
59717 return MA_INVALID_ARGS;
59718 }
59719
59720 pDataSourceBase->pCurrent = pCurrentDataSource;
59721
59722 return MA_SUCCESS;
59723}
59724
59726{
59727 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
59728
59729 if (pDataSource == NULL) {
59730 return NULL;
59731 }
59732
59733 return pDataSourceBase->pCurrent;
59734}
59735
59737{
59738 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
59739
59740 if (pDataSource == NULL) {
59741 return MA_INVALID_ARGS;
59742 }
59743
59744 pDataSourceBase->pNext = pNextDataSource;
59745
59746 return MA_SUCCESS;
59747}
59748
59750{
59751 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
59752
59753 if (pDataSource == NULL) {
59754 return NULL;
59755 }
59756
59757 return pDataSourceBase->pNext;
59758}
59759
59761{
59762 ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
59763
59764 if (pDataSource == NULL) {
59765 return MA_INVALID_ARGS;
59766 }
59767
59768 pDataSourceBase->onGetNext = onGetNext;
59769
59770 return MA_SUCCESS;
59771}
59772
59774{
59775 const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
59776
59777 if (pDataSource == NULL) {
59778 return NULL;
59779 }
59780
59781 return pDataSourceBase->onGetNext;
59782}
59783
59784
59785static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
59786{
59787 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
59788 ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames(pAudioBufferRef, pFramesOut, frameCount, MA_FALSE);
59789
59790 if (pFramesRead != NULL) {
59791 *pFramesRead = framesRead;
59792 }
59793
59794 if (framesRead < frameCount || framesRead == 0) {
59795 return MA_AT_END;
59796 }
59797
59798 return MA_SUCCESS;
59799}
59800
59801static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
59802{
59803 return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex);
59804}
59805
59806static 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)
59807{
59808 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
59809
59810 *pFormat = pAudioBufferRef->format;
59811 *pChannels = pAudioBufferRef->channels;
59812 *pSampleRate = pAudioBufferRef->sampleRate;
59813 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels);
59814
59815 return MA_SUCCESS;
59816}
59817
59818static ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
59819{
59820 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
59821
59822 *pCursor = pAudioBufferRef->cursor;
59823
59824 return MA_SUCCESS;
59825}
59826
59827static ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
59828{
59829 ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
59830
59831 *pLength = pAudioBufferRef->sizeInFrames;
59832
59833 return MA_SUCCESS;
59834}
59835
59836static ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable =
59837{
59838 ma_audio_buffer_ref__data_source_on_read,
59839 ma_audio_buffer_ref__data_source_on_seek,
59840 ma_audio_buffer_ref__data_source_on_get_data_format,
59841 ma_audio_buffer_ref__data_source_on_get_cursor,
59842 ma_audio_buffer_ref__data_source_on_get_length,
59843 NULL, /* onSetLooping */
59844 0
59845};
59846
59847MA_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)
59848{
59849 ma_result result;
59850 ma_data_source_config dataSourceConfig;
59851
59852 if (pAudioBufferRef == NULL) {
59853 return MA_INVALID_ARGS;
59854 }
59855
59856 MA_ZERO_OBJECT(pAudioBufferRef);
59857
59858 dataSourceConfig = ma_data_source_config_init();
59859 dataSourceConfig.vtable = &g_ma_audio_buffer_ref_data_source_vtable;
59860
59861 result = ma_data_source_init(&dataSourceConfig, &pAudioBufferRef->ds);
59862 if (result != MA_SUCCESS) {
59863 return result;
59864 }
59865
59866 pAudioBufferRef->format = format;
59867 pAudioBufferRef->channels = channels;
59868 pAudioBufferRef->sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */
59869 pAudioBufferRef->cursor = 0;
59870 pAudioBufferRef->sizeInFrames = sizeInFrames;
59871 pAudioBufferRef->pData = pData;
59872
59873 return MA_SUCCESS;
59874}
59875
59876MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef)
59877{
59878 if (pAudioBufferRef == NULL) {
59879 return;
59880 }
59881
59882 ma_data_source_uninit(&pAudioBufferRef->ds);
59883}
59884
59885MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames)
59886{
59887 if (pAudioBufferRef == NULL) {
59888 return MA_INVALID_ARGS;
59889 }
59890
59891 pAudioBufferRef->cursor = 0;
59892 pAudioBufferRef->sizeInFrames = sizeInFrames;
59893 pAudioBufferRef->pData = pData;
59894
59895 return MA_SUCCESS;
59896}
59897
59898MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop)
59899{
59900 ma_uint64 totalFramesRead = 0;
59901
59902 if (pAudioBufferRef == NULL) {
59903 return 0;
59904 }
59905
59906 if (frameCount == 0) {
59907 return 0;
59908 }
59909
59910 while (totalFramesRead < frameCount) {
59911 ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
59912 ma_uint64 framesRemaining = frameCount - totalFramesRead;
59913 ma_uint64 framesToRead;
59914
59915 framesToRead = framesRemaining;
59916 if (framesToRead > framesAvailable) {
59917 framesToRead = framesAvailable;
59918 }
59919
59920 if (pFramesOut != NULL) {
59921 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);
59922 }
59923
59924 totalFramesRead += framesToRead;
59925
59926 pAudioBufferRef->cursor += framesToRead;
59927 if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) {
59928 if (loop) {
59929 pAudioBufferRef->cursor = 0;
59930 } else {
59931 break; /* We've reached the end and we're not looping. Done. */
59932 }
59933 }
59934
59935 MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames);
59936 }
59937
59938 return totalFramesRead;
59939}
59940
59941MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex)
59942{
59943 if (pAudioBufferRef == NULL) {
59944 return MA_INVALID_ARGS;
59945 }
59946
59947 if (frameIndex > pAudioBufferRef->sizeInFrames) {
59948 return MA_INVALID_ARGS;
59949 }
59950
59951 pAudioBufferRef->cursor = (size_t)frameIndex;
59952
59953 return MA_SUCCESS;
59954}
59955
59956MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount)
59957{
59958 ma_uint64 framesAvailable;
59959 ma_uint64 frameCount = 0;
59960
59961 if (ppFramesOut != NULL) {
59962 *ppFramesOut = NULL; /* Safety. */
59963 }
59964
59965 if (pFrameCount != NULL) {
59966 frameCount = *pFrameCount;
59967 *pFrameCount = 0; /* Safety. */
59968 }
59969
59970 if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) {
59971 return MA_INVALID_ARGS;
59972 }
59973
59974 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
59975 if (frameCount > framesAvailable) {
59976 frameCount = framesAvailable;
59977 }
59978
59979 *ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels));
59980 *pFrameCount = frameCount;
59981
59982 return MA_SUCCESS;
59983}
59984
59985MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount)
59986{
59987 ma_uint64 framesAvailable;
59988
59989 if (pAudioBufferRef == NULL) {
59990 return MA_INVALID_ARGS;
59991 }
59992
59993 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
59994 if (frameCount > framesAvailable) {
59995 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. */
59996 }
59997
59998 pAudioBufferRef->cursor += frameCount;
59999
60000 if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) {
60001 return MA_AT_END; /* Successful. Need to tell the caller that the end has been reached so that it can loop if desired. */
60002 } else {
60003 return MA_SUCCESS;
60004 }
60005}
60006
60007MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef)
60008{
60009 if (pAudioBufferRef == NULL) {
60010 return MA_FALSE;
60011 }
60012
60013 return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames;
60014}
60015
60016MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor)
60017{
60018 if (pCursor == NULL) {
60019 return MA_INVALID_ARGS;
60020 }
60021
60022 *pCursor = 0;
60023
60024 if (pAudioBufferRef == NULL) {
60025 return MA_INVALID_ARGS;
60026 }
60027
60028 *pCursor = pAudioBufferRef->cursor;
60029
60030 return MA_SUCCESS;
60031}
60032
60033MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength)
60034{
60035 if (pLength == NULL) {
60036 return MA_INVALID_ARGS;
60037 }
60038
60039 *pLength = 0;
60040
60041 if (pAudioBufferRef == NULL) {
60042 return MA_INVALID_ARGS;
60043 }
60044
60045 *pLength = pAudioBufferRef->sizeInFrames;
60046
60047 return MA_SUCCESS;
60048}
60049
60050MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames)
60051{
60052 if (pAvailableFrames == NULL) {
60053 return MA_INVALID_ARGS;
60054 }
60055
60056 *pAvailableFrames = 0;
60057
60058 if (pAudioBufferRef == NULL) {
60059 return MA_INVALID_ARGS;
60060 }
60061
60062 if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) {
60063 *pAvailableFrames = 0;
60064 } else {
60065 *pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
60066 }
60067
60068 return MA_SUCCESS;
60069}
60070
60071
60072
60073
60074MA_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)
60075{
60076 ma_audio_buffer_config config;
60077
60078 MA_ZERO_OBJECT(&config);
60079 config.format = format;
60080 config.channels = channels;
60081 config.sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */
60082 config.sizeInFrames = sizeInFrames;
60083 config.pData = pData;
60084 ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks);
60085
60086 return config;
60087}
60088
60089static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer)
60090{
60091 ma_result result;
60092
60093 if (pAudioBuffer == NULL) {
60094 return MA_INVALID_ARGS;
60095 }
60096
60097 MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData)); /* Safety. Don't overwrite the extra data. */
60098
60099 if (pConfig == NULL) {
60100 return MA_INVALID_ARGS;
60101 }
60102
60103 if (pConfig->sizeInFrames == 0) {
60104 return MA_INVALID_ARGS; /* Not allowing buffer sizes of 0 frames. */
60105 }
60106
60107 result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref);
60108 if (result != MA_SUCCESS) {
60109 return result;
60110 }
60111
60112 /* TODO: Version 0.12. Set this in ma_audio_buffer_ref_init() instead of here. */
60113 pAudioBuffer->ref.sampleRate = pConfig->sampleRate;
60114
60115 ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks);
60116
60117 if (doCopy) {
60118 ma_uint64 allocationSizeInBytes;
60119 void* pData;
60120
60121 allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels);
60122 if (allocationSizeInBytes > MA_SIZE_MAX) {
60123 return MA_OUT_OF_MEMORY; /* Too big. */
60124 }
60125
60126 pData = ma_malloc((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */
60127 if (pData == NULL) {
60128 return MA_OUT_OF_MEMORY;
60129 }
60130
60131 if (pConfig->pData != NULL) {
60132 ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
60133 } else {
60134 ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
60135 }
60136
60137 ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames);
60138 pAudioBuffer->ownsData = MA_TRUE;
60139 } else {
60140 ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames);
60141 pAudioBuffer->ownsData = MA_FALSE;
60142 }
60143
60144 return MA_SUCCESS;
60145}
60146
60147static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree)
60148{
60149 if (pAudioBuffer == NULL) {
60150 return;
60151 }
60152
60153 if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) {
60154 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. */
60155 }
60156
60157 if (doFree) {
60158 ma_free(pAudioBuffer, &pAudioBuffer->allocationCallbacks);
60159 }
60160
60161 ma_audio_buffer_ref_uninit(&pAudioBuffer->ref);
60162}
60163
60164MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer)
60165{
60166 return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer);
60167}
60168
60169MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer)
60170{
60171 return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer);
60172}
60173
60174MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer)
60175{
60176 ma_result result;
60177 ma_audio_buffer* pAudioBuffer;
60178 ma_audio_buffer_config innerConfig; /* We'll be making some changes to the config, so need to make a copy. */
60179 ma_uint64 allocationSizeInBytes;
60180
60181 if (ppAudioBuffer == NULL) {
60182 return MA_INVALID_ARGS;
60183 }
60184
60185 *ppAudioBuffer = NULL; /* Safety. */
60186
60187 if (pConfig == NULL) {
60188 return MA_INVALID_ARGS;
60189 }
60190
60191 innerConfig = *pConfig;
60192 ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks);
60193
60194 allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels));
60195 if (allocationSizeInBytes > MA_SIZE_MAX) {
60196 return MA_OUT_OF_MEMORY; /* Too big. */
60197 }
60198
60199 pAudioBuffer = (ma_audio_buffer*)ma_malloc((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */
60200 if (pAudioBuffer == NULL) {
60201 return MA_OUT_OF_MEMORY;
60202 }
60203
60204 if (pConfig->pData != NULL) {
60205 ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
60206 } else {
60207 ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels);
60208 }
60209
60210 innerConfig.pData = &pAudioBuffer->_pExtraData[0];
60211
60212 result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer);
60213 if (result != MA_SUCCESS) {
60214 ma_free(pAudioBuffer, &innerConfig.allocationCallbacks);
60215 return result;
60216 }
60217
60218 *ppAudioBuffer = pAudioBuffer;
60219
60220 return MA_SUCCESS;
60221}
60222
60223MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer)
60224{
60225 ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE);
60226}
60227
60228MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer)
60229{
60230 ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE);
60231}
60232
60233MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop)
60234{
60235 if (pAudioBuffer == NULL) {
60236 return 0;
60237 }
60238
60239 return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop);
60240}
60241
60242MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex)
60243{
60244 if (pAudioBuffer == NULL) {
60245 return MA_INVALID_ARGS;
60246 }
60247
60248 return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex);
60249}
60250
60251MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount)
60252{
60253 if (ppFramesOut != NULL) {
60254 *ppFramesOut = NULL; /* Safety. */
60255 }
60256
60257 if (pAudioBuffer == NULL) {
60258 if (pFrameCount != NULL) {
60259 *pFrameCount = 0;
60260 }
60261
60262 return MA_INVALID_ARGS;
60263 }
60264
60265 return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount);
60266}
60267
60268MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount)
60269{
60270 if (pAudioBuffer == NULL) {
60271 return MA_INVALID_ARGS;
60272 }
60273
60274 return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount);
60275}
60276
60277MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer)
60278{
60279 if (pAudioBuffer == NULL) {
60280 return MA_FALSE;
60281 }
60282
60283 return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref);
60284}
60285
60286MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor)
60287{
60288 if (pAudioBuffer == NULL) {
60289 return MA_INVALID_ARGS;
60290 }
60291
60292 return ma_audio_buffer_ref_get_cursor_in_pcm_frames(&pAudioBuffer->ref, pCursor);
60293}
60294
60295MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength)
60296{
60297 if (pAudioBuffer == NULL) {
60298 return MA_INVALID_ARGS;
60299 }
60300
60301 return ma_audio_buffer_ref_get_length_in_pcm_frames(&pAudioBuffer->ref, pLength);
60302}
60303
60304MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames)
60305{
60306 if (pAvailableFrames == NULL) {
60307 return MA_INVALID_ARGS;
60308 }
60309
60310 *pAvailableFrames = 0;
60311
60312 if (pAudioBuffer == NULL) {
60313 return MA_INVALID_ARGS;
60314 }
60315
60316 return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames);
60317}
60318
60319
60320
60321
60322
60323MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData)
60324{
60325 if (pData == NULL) {
60326 return MA_INVALID_ARGS;
60327 }
60328
60329 MA_ZERO_OBJECT(pData);
60330
60331 pData->format = format;
60332 pData->channels = channels;
60333 pData->pTail = &pData->head;
60334
60335 return MA_SUCCESS;
60336}
60337
60338MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks)
60339{
60341
60342 if (pData == NULL) {
60343 return;
60344 }
60345
60346 /* All pages need to be freed. */
60347 pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext);
60348 while (pPage != NULL) {
60349 ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext);
60350
60351 ma_free(pPage, pAllocationCallbacks);
60352 pPage = pNext;
60353 }
60354}
60355
60357{
60358 if (pData == NULL) {
60359 return NULL;
60360 }
60361
60362 return &pData->head;
60363}
60364
60366{
60367 if (pData == NULL) {
60368 return NULL;
60369 }
60370
60371 return pData->pTail;
60372}
60373
60374MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength)
60375{
60377
60378 if (pLength == NULL) {
60379 return MA_INVALID_ARGS;
60380 }
60381
60382 *pLength = 0;
60383
60384 if (pData == NULL) {
60385 return MA_INVALID_ARGS;
60386 }
60387
60388 /* Calculate the length from the linked list. */
60389 for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) {
60390 *pLength += pPage->sizeInFrames;
60391 }
60392
60393 return MA_SUCCESS;
60394}
60395
60396MA_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)
60397{
60399 ma_uint64 allocationSize;
60400
60401 if (ppPage == NULL) {
60402 return MA_INVALID_ARGS;
60403 }
60404
60405 *ppPage = NULL;
60406
60407 if (pData == NULL) {
60408 return MA_INVALID_ARGS;
60409 }
60410
60411 allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels));
60412 if (allocationSize > MA_SIZE_MAX) {
60413 return MA_OUT_OF_MEMORY; /* Too big. */
60414 }
60415
60416 pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */
60417 if (pPage == NULL) {
60418 return MA_OUT_OF_MEMORY;
60419 }
60420
60421 pPage->pNext = NULL;
60422 pPage->sizeInFrames = pageSizeInFrames;
60423
60424 if (pInitialData != NULL) {
60425 ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels);
60426 }
60427
60428 *ppPage = pPage;
60429
60430 return MA_SUCCESS;
60431}
60432
60433MA_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)
60434{
60435 if (pData == NULL || pPage == NULL) {
60436 return MA_INVALID_ARGS;
60437 }
60438
60439 /* It's assumed the page is not attached to the list. */
60440 ma_free(pPage, pAllocationCallbacks);
60441
60442 return MA_SUCCESS;
60443}
60444
60446{
60447 if (pData == NULL || pPage == NULL) {
60448 return MA_INVALID_ARGS;
60449 }
60450
60451 /* 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. */
60452
60453 /* First thing to do is update the tail. */
60454 for (;;) {
60455 ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->pTail);
60456 ma_paged_audio_buffer_page* pNewTail = pPage;
60457
60458 if (ma_atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) {
60459 /* 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. */
60460 ma_atomic_exchange_ptr(&pOldTail->pNext, pPage);
60461 break; /* Done. */
60462 }
60463 }
60464
60465 return MA_SUCCESS;
60466}
60467
60468MA_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)
60469{
60470 ma_result result;
60472
60473 result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage);
60474 if (result != MA_SUCCESS) {
60475 return result;
60476 }
60477
60478 return ma_paged_audio_buffer_data_append_page(pData, pPage); /* <-- Should never fail. */
60479}
60480
60481
60482MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData)
60483{
60484 ma_paged_audio_buffer_config config;
60485
60486 MA_ZERO_OBJECT(&config);
60487 config.pData = pData;
60488
60489 return config;
60490}
60491
60492
60493static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
60494{
60495 return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead);
60496}
60497
60498static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
60499{
60500 return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex);
60501}
60502
60503static 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)
60504{
60505 ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource;
60506
60507 *pFormat = pPagedAudioBuffer->pData->format;
60508 *pChannels = pPagedAudioBuffer->pData->channels;
60509 *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */
60510 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels);
60511
60512 return MA_SUCCESS;
60513}
60514
60515static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
60516{
60517 return ma_paged_audio_buffer_get_cursor_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pCursor);
60518}
60519
60520static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
60521{
60522 return ma_paged_audio_buffer_get_length_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pLength);
60523}
60524
60525static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable =
60526{
60527 ma_paged_audio_buffer__data_source_on_read,
60528 ma_paged_audio_buffer__data_source_on_seek,
60529 ma_paged_audio_buffer__data_source_on_get_data_format,
60530 ma_paged_audio_buffer__data_source_on_get_cursor,
60531 ma_paged_audio_buffer__data_source_on_get_length,
60532 NULL, /* onSetLooping */
60533 0
60534};
60535
60536MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer)
60537{
60538 ma_result result;
60539 ma_data_source_config dataSourceConfig;
60540
60541 if (pPagedAudioBuffer == NULL) {
60542 return MA_INVALID_ARGS;
60543 }
60544
60545 MA_ZERO_OBJECT(pPagedAudioBuffer);
60546
60547 /* A config is required for the format and channel count. */
60548 if (pConfig == NULL) {
60549 return MA_INVALID_ARGS;
60550 }
60551
60552 if (pConfig->pData == NULL) {
60553 return MA_INVALID_ARGS; /* No underlying data specified. */
60554 }
60555
60556 dataSourceConfig = ma_data_source_config_init();
60557 dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable;
60558
60559 result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds);
60560 if (result != MA_SUCCESS) {
60561 return result;
60562 }
60563
60564 pPagedAudioBuffer->pData = pConfig->pData;
60565 pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData);
60566 pPagedAudioBuffer->relativeCursor = 0;
60567 pPagedAudioBuffer->absoluteCursor = 0;
60568
60569 return MA_SUCCESS;
60570}
60571
60572MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer)
60573{
60574 if (pPagedAudioBuffer == NULL) {
60575 return;
60576 }
60577
60578 /* Nothing to do. The data needs to be deleted separately. */
60579}
60580
60581MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
60582{
60583 ma_result result = MA_SUCCESS;
60584 ma_uint64 totalFramesRead = 0;
60585 ma_format format;
60586 ma_uint32 channels;
60587
60588 if (pPagedAudioBuffer == NULL) {
60589 return MA_INVALID_ARGS;
60590 }
60591
60592 format = pPagedAudioBuffer->pData->format;
60593 channels = pPagedAudioBuffer->pData->channels;
60594
60595 while (totalFramesRead < frameCount) {
60596 /* Read from the current page. The buffer should never be in a state where this is NULL. */
60597 ma_uint64 framesRemainingInCurrentPage;
60598 ma_uint64 framesRemainingToRead = frameCount - totalFramesRead;
60599 ma_uint64 framesToReadThisIteration;
60600
60601 MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL);
60602
60603 framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor;
60604
60605 framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead);
60606 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);
60607 totalFramesRead += framesToReadThisIteration;
60608
60609 pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration;
60610 pPagedAudioBuffer->relativeCursor += framesToReadThisIteration;
60611
60612 /* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */
60613 MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames);
60614
60615 if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) {
60616 /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */
60617 ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext);
60618 if (pNext == NULL) {
60619 result = MA_AT_END;
60620 break; /* We've reached the end. */
60621 } else {
60622 pPagedAudioBuffer->pCurrent = pNext;
60623 pPagedAudioBuffer->relativeCursor = 0;
60624 }
60625 }
60626 }
60627
60628 if (pFramesRead != NULL) {
60629 *pFramesRead = totalFramesRead;
60630 }
60631
60632 return result;
60633}
60634
60635MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex)
60636{
60637 if (pPagedAudioBuffer == NULL) {
60638 return MA_INVALID_ARGS;
60639 }
60640
60641 if (frameIndex == pPagedAudioBuffer->absoluteCursor) {
60642 return MA_SUCCESS; /* Nothing to do. */
60643 }
60644
60645 if (frameIndex < pPagedAudioBuffer->absoluteCursor) {
60646 /* Moving backwards. Need to move the cursor back to the start, and then move forward. */
60647 pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData);
60648 pPagedAudioBuffer->absoluteCursor = 0;
60649 pPagedAudioBuffer->relativeCursor = 0;
60650
60651 /* Fall through to the forward seeking section below. */
60652 }
60653
60654 if (frameIndex > pPagedAudioBuffer->absoluteCursor) {
60655 /* Moving forward. */
60657 ma_uint64 runningCursor = 0;
60658
60659 for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) {
60660 ma_uint64 pageRangeBeg = runningCursor;
60661 ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames;
60662
60663 if (frameIndex >= pageRangeBeg) {
60664 if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */
60665 /* We found the page. */
60666 pPagedAudioBuffer->pCurrent = pPage;
60667 pPagedAudioBuffer->absoluteCursor = frameIndex;
60668 pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg;
60669 return MA_SUCCESS;
60670 }
60671 }
60672
60673 runningCursor = pageRangeEnd;
60674 }
60675
60676 /* Getting here means we tried seeking too far forward. Don't change any state. */
60677 return MA_BAD_SEEK;
60678 }
60679
60680 return MA_SUCCESS;
60681}
60682
60683MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor)
60684{
60685 if (pCursor == NULL) {
60686 return MA_INVALID_ARGS;
60687 }
60688
60689 *pCursor = 0; /* Safety. */
60690
60691 if (pPagedAudioBuffer == NULL) {
60692 return MA_INVALID_ARGS;
60693 }
60694
60695 *pCursor = pPagedAudioBuffer->absoluteCursor;
60696
60697 return MA_SUCCESS;
60698}
60699
60700MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength)
60701{
60702 return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength);
60703}
60704
60705
60706
60707/**************************************************************************************************************************************************************
60708
60709VFS
60710
60711**************************************************************************************************************************************************************/
60712MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
60713{
60714 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
60715
60716 if (pFile == NULL) {
60717 return MA_INVALID_ARGS;
60718 }
60719
60720 *pFile = NULL;
60721
60722 if (pVFS == NULL || pFilePath == NULL || openMode == 0) {
60723 return MA_INVALID_ARGS;
60724 }
60725
60726 if (pCallbacks->onOpen == NULL) {
60727 return MA_NOT_IMPLEMENTED;
60728 }
60729
60730 return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile);
60731}
60732
60733MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
60734{
60735 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
60736
60737 if (pFile == NULL) {
60738 return MA_INVALID_ARGS;
60739 }
60740
60741 *pFile = NULL;
60742
60743 if (pVFS == NULL || pFilePath == NULL || openMode == 0) {
60744 return MA_INVALID_ARGS;
60745 }
60746
60747 if (pCallbacks->onOpenW == NULL) {
60748 return MA_NOT_IMPLEMENTED;
60749 }
60750
60751 return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile);
60752}
60753
60755{
60756 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
60757
60758 if (pVFS == NULL || file == NULL) {
60759 return MA_INVALID_ARGS;
60760 }
60761
60762 if (pCallbacks->onClose == NULL) {
60763 return MA_NOT_IMPLEMENTED;
60764 }
60765
60766 return pCallbacks->onClose(pVFS, file);
60767}
60768
60769MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
60770{
60771 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
60772 ma_result result;
60773 size_t bytesRead = 0;
60774
60775 if (pBytesRead != NULL) {
60776 *pBytesRead = 0;
60777 }
60778
60779 if (pVFS == NULL || file == NULL || pDst == NULL) {
60780 return MA_INVALID_ARGS;
60781 }
60782
60783 if (pCallbacks->onRead == NULL) {
60784 return MA_NOT_IMPLEMENTED;
60785 }
60786
60787 result = pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, &bytesRead);
60788
60789 if (pBytesRead != NULL) {
60790 *pBytesRead = bytesRead;
60791 }
60792
60793 if (result == MA_SUCCESS && bytesRead == 0 && sizeInBytes > 0) {
60794 result = MA_AT_END;
60795 }
60796
60797 return result;
60798}
60799
60800MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
60801{
60802 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
60803
60804 if (pBytesWritten != NULL) {
60805 *pBytesWritten = 0;
60806 }
60807
60808 if (pVFS == NULL || file == NULL || pSrc == NULL) {
60809 return MA_INVALID_ARGS;
60810 }
60811
60812 if (pCallbacks->onWrite == NULL) {
60813 return MA_NOT_IMPLEMENTED;
60814 }
60815
60816 return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
60817}
60818
60820{
60821 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
60822
60823 if (pVFS == NULL || file == NULL) {
60824 return MA_INVALID_ARGS;
60825 }
60826
60827 if (pCallbacks->onSeek == NULL) {
60828 return MA_NOT_IMPLEMENTED;
60829 }
60830
60831 return pCallbacks->onSeek(pVFS, file, offset, origin);
60832}
60833
60835{
60836 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
60837
60838 if (pCursor == NULL) {
60839 return MA_INVALID_ARGS;
60840 }
60841
60842 *pCursor = 0;
60843
60844 if (pVFS == NULL || file == NULL) {
60845 return MA_INVALID_ARGS;
60846 }
60847
60848 if (pCallbacks->onTell == NULL) {
60849 return MA_NOT_IMPLEMENTED;
60850 }
60851
60852 return pCallbacks->onTell(pVFS, file, pCursor);
60853}
60854
60855MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
60856{
60857 ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
60858
60859 if (pInfo == NULL) {
60860 return MA_INVALID_ARGS;
60861 }
60862
60863 MA_ZERO_OBJECT(pInfo);
60864
60865 if (pVFS == NULL || file == NULL) {
60866 return MA_INVALID_ARGS;
60867 }
60868
60869 if (pCallbacks->onInfo == NULL) {
60870 return MA_NOT_IMPLEMENTED;
60871 }
60872
60873 return pCallbacks->onInfo(pVFS, file, pInfo);
60874}
60875
60876
60877#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX))
60878 #define MA_USE_WIN32_FILEIO
60879#endif
60880
60881#if defined(MA_USE_WIN32_FILEIO)
60882/*
60883We need to dynamically load SetFilePointer or SetFilePointerEx because older versions of Windows do
60884not have the Ex version. We therefore need to do some dynamic branching depending on what's available.
60885
60886We load these when we load our first file from the default VFS. It's left open for the life of the
60887program and is left to the OS to uninitialize when the program terminates.
60888*/
60889typedef DWORD (__stdcall * ma_SetFilePointer_proc)(HANDLE hFile, LONG lDistanceToMove, LONG* lpDistanceToMoveHigh, DWORD dwMoveMethod);
60890typedef BOOL (__stdcall * ma_SetFilePointerEx_proc)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, LARGE_INTEGER* lpNewFilePointer, DWORD dwMoveMethod);
60891
60892static ma_handle hKernel32DLL = NULL;
60893static ma_SetFilePointer_proc ma_SetFilePointer = NULL;
60894static ma_SetFilePointerEx_proc ma_SetFilePointerEx = NULL;
60895
60896static void ma_win32_fileio_init(void)
60897{
60898 if (hKernel32DLL == NULL) {
60899 hKernel32DLL = ma_dlopen(NULL, "kernel32.dll");
60900 if (hKernel32DLL != NULL) {
60901 ma_SetFilePointer = (ma_SetFilePointer_proc) ma_dlsym(NULL, hKernel32DLL, "SetFilePointer");
60902 ma_SetFilePointerEx = (ma_SetFilePointerEx_proc)ma_dlsym(NULL, hKernel32DLL, "SetFilePointerEx");
60903 }
60904 }
60905}
60906
60907static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition)
60908{
60909 *pDesiredAccess = 0;
60910 if ((openMode & MA_OPEN_MODE_READ) != 0) {
60911 *pDesiredAccess |= GENERIC_READ;
60912 }
60913 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
60914 *pDesiredAccess |= GENERIC_WRITE;
60915 }
60916
60917 *pShareMode = 0;
60918 if ((openMode & MA_OPEN_MODE_READ) != 0) {
60919 *pShareMode |= FILE_SHARE_READ;
60920 }
60921
60922 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
60923 *pCreationDisposition = CREATE_ALWAYS; /* Opening in write mode. Truncate. */
60924 } else {
60925 *pCreationDisposition = OPEN_EXISTING; /* Opening in read mode. File must exist. */
60926 }
60927}
60928
60929static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
60930{
60931 HANDLE hFile;
60932 DWORD dwDesiredAccess;
60933 DWORD dwShareMode;
60934 DWORD dwCreationDisposition;
60935
60936 (void)pVFS;
60937
60938 /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */
60939 ma_win32_fileio_init();
60940
60941 ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);
60942
60943 hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
60944 if (hFile == INVALID_HANDLE_VALUE) {
60945 return ma_result_from_GetLastError(GetLastError());
60946 }
60947
60948 *pFile = hFile;
60949 return MA_SUCCESS;
60950}
60951
60952static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
60953{
60954 HANDLE hFile;
60955 DWORD dwDesiredAccess;
60956 DWORD dwShareMode;
60957 DWORD dwCreationDisposition;
60958
60959 (void)pVFS;
60960
60961 /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */
60962 ma_win32_fileio_init();
60963
60964 ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);
60965
60966 hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
60967 if (hFile == INVALID_HANDLE_VALUE) {
60968 return ma_result_from_GetLastError(GetLastError());
60969 }
60970
60971 *pFile = hFile;
60972 return MA_SUCCESS;
60973}
60974
60975static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file)
60976{
60977 (void)pVFS;
60978
60979 if (CloseHandle((HANDLE)file) == 0) {
60980 return ma_result_from_GetLastError(GetLastError());
60981 }
60982
60983 return MA_SUCCESS;
60984}
60985
60986
60987static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
60988{
60989 ma_result result = MA_SUCCESS;
60990 size_t totalBytesRead;
60991
60992 (void)pVFS;
60993
60994 totalBytesRead = 0;
60995 while (totalBytesRead < sizeInBytes) {
60996 size_t bytesRemaining;
60997 DWORD bytesToRead;
60998 DWORD bytesRead;
60999 BOOL readResult;
61000
61001 bytesRemaining = sizeInBytes - totalBytesRead;
61002 if (bytesRemaining >= 0xFFFFFFFF) {
61003 bytesToRead = 0xFFFFFFFF;
61004 } else {
61005 bytesToRead = (DWORD)bytesRemaining;
61006 }
61007
61008 readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL);
61009 if (readResult == 1 && bytesRead == 0) {
61010 result = MA_AT_END;
61011 break; /* EOF */
61012 }
61013
61014 totalBytesRead += bytesRead;
61015
61016 if (bytesRead < bytesToRead) {
61017 break; /* EOF */
61018 }
61019
61020 if (readResult == 0) {
61021 result = ma_result_from_GetLastError(GetLastError());
61022 break;
61023 }
61024 }
61025
61026 if (pBytesRead != NULL) {
61027 *pBytesRead = totalBytesRead;
61028 }
61029
61030 return result;
61031}
61032
61033static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
61034{
61035 ma_result result = MA_SUCCESS;
61036 size_t totalBytesWritten;
61037
61038 (void)pVFS;
61039
61040 totalBytesWritten = 0;
61041 while (totalBytesWritten < sizeInBytes) {
61042 size_t bytesRemaining;
61043 DWORD bytesToWrite;
61044 DWORD bytesWritten;
61045 BOOL writeResult;
61046
61047 bytesRemaining = sizeInBytes - totalBytesWritten;
61048 if (bytesRemaining >= 0xFFFFFFFF) {
61049 bytesToWrite = 0xFFFFFFFF;
61050 } else {
61051 bytesToWrite = (DWORD)bytesRemaining;
61052 }
61053
61054 writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL);
61055 totalBytesWritten += bytesWritten;
61056
61057 if (writeResult == 0) {
61058 result = ma_result_from_GetLastError(GetLastError());
61059 break;
61060 }
61061 }
61062
61063 if (pBytesWritten != NULL) {
61064 *pBytesWritten = totalBytesWritten;
61065 }
61066
61067 return result;
61068}
61069
61070
61071static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
61072{
61073 LARGE_INTEGER liDistanceToMove;
61074 DWORD dwMoveMethod;
61075 BOOL result;
61076
61077 (void)pVFS;
61078
61079 liDistanceToMove.QuadPart = offset;
61080
61081 /* */ if (origin == ma_seek_origin_current) {
61082 dwMoveMethod = FILE_CURRENT;
61083 } else if (origin == ma_seek_origin_end) {
61084 dwMoveMethod = FILE_END;
61085 } else {
61086 dwMoveMethod = FILE_BEGIN;
61087 }
61088
61089 if (ma_SetFilePointerEx != NULL) {
61090 result = ma_SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod);
61091 } else if (ma_SetFilePointer != NULL) {
61092 /* No SetFilePointerEx() so restrict to 31 bits. */
61093 if (offset > 0x7FFFFFFF) {
61094 return MA_OUT_OF_RANGE;
61095 }
61096
61097 result = ma_SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod);
61098 } else {
61099 return MA_NOT_IMPLEMENTED;
61100 }
61101
61102 if (result == 0) {
61103 return ma_result_from_GetLastError(GetLastError());
61104 }
61105
61106 return MA_SUCCESS;
61107}
61108
61109static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
61110{
61111 LARGE_INTEGER liZero;
61112 LARGE_INTEGER liTell;
61113 BOOL result;
61114
61115 (void)pVFS;
61116
61117 liZero.QuadPart = 0;
61118
61119 if (ma_SetFilePointerEx != NULL) {
61120 result = ma_SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT);
61121 } else if (ma_SetFilePointer != NULL) {
61122 LONG tell;
61123
61124 result = ma_SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT);
61125 liTell.QuadPart = tell;
61126 } else {
61127 return MA_NOT_IMPLEMENTED;
61128 }
61129
61130 if (result == 0) {
61131 return ma_result_from_GetLastError(GetLastError());
61132 }
61133
61134 if (pCursor != NULL) {
61135 *pCursor = liTell.QuadPart;
61136 }
61137
61138 return MA_SUCCESS;
61139}
61140
61141static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
61142{
61143 BY_HANDLE_FILE_INFORMATION fi;
61144 BOOL result;
61145
61146 (void)pVFS;
61147
61148 result = GetFileInformationByHandle((HANDLE)file, &fi);
61149 if (result == 0) {
61150 return ma_result_from_GetLastError(GetLastError());
61151 }
61152
61153 pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow);
61154
61155 return MA_SUCCESS;
61156}
61157#else
61158static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
61159{
61160 ma_result result;
61161 FILE* pFileStd;
61162 const char* pOpenModeStr;
61163
61164 MA_ASSERT(pFilePath != NULL);
61165 MA_ASSERT(openMode != 0);
61166 MA_ASSERT(pFile != NULL);
61167
61168 (void)pVFS;
61169
61170 if ((openMode & MA_OPEN_MODE_READ) != 0) {
61171 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
61172 pOpenModeStr = "r+";
61173 } else {
61174 pOpenModeStr = "rb";
61175 }
61176 } else {
61177 pOpenModeStr = "wb";
61178 }
61179
61180 result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr);
61181 if (result != MA_SUCCESS) {
61182 return result;
61183 }
61184
61185 *pFile = pFileStd;
61186
61187 return MA_SUCCESS;
61188}
61189
61190static ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
61191{
61192 ma_result result;
61193 FILE* pFileStd;
61194 const wchar_t* pOpenModeStr;
61195
61196 MA_ASSERT(pFilePath != NULL);
61197 MA_ASSERT(openMode != 0);
61198 MA_ASSERT(pFile != NULL);
61199
61200 (void)pVFS;
61201
61202 if ((openMode & MA_OPEN_MODE_READ) != 0) {
61203 if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
61204 pOpenModeStr = L"r+";
61205 } else {
61206 pOpenModeStr = L"rb";
61207 }
61208 } else {
61209 pOpenModeStr = L"wb";
61210 }
61211
61212 result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL);
61213 if (result != MA_SUCCESS) {
61214 return result;
61215 }
61216
61217 *pFile = pFileStd;
61218
61219 return MA_SUCCESS;
61220}
61221
61222static ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file)
61223{
61224 MA_ASSERT(file != NULL);
61225
61226 (void)pVFS;
61227
61228 fclose((FILE*)file);
61229
61230 return MA_SUCCESS;
61231}
61232
61233static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
61234{
61235 size_t result;
61236
61237 MA_ASSERT(file != NULL);
61238 MA_ASSERT(pDst != NULL);
61239
61240 (void)pVFS;
61241
61242 result = fread(pDst, 1, sizeInBytes, (FILE*)file);
61243
61244 if (pBytesRead != NULL) {
61245 *pBytesRead = result;
61246 }
61247
61248 if (result != sizeInBytes) {
61249 if (result == 0 && feof((FILE*)file)) {
61250 return MA_AT_END;
61251 } else {
61252 return ma_result_from_errno(ferror((FILE*)file));
61253 }
61254 }
61255
61256 return MA_SUCCESS;
61257}
61258
61259static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
61260{
61261 size_t result;
61262
61263 MA_ASSERT(file != NULL);
61264 MA_ASSERT(pSrc != NULL);
61265
61266 (void)pVFS;
61267
61268 result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file);
61269
61270 if (pBytesWritten != NULL) {
61271 *pBytesWritten = result;
61272 }
61273
61274 if (result != sizeInBytes) {
61275 return ma_result_from_errno(ferror((FILE*)file));
61276 }
61277
61278 return MA_SUCCESS;
61279}
61280
61281static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
61282{
61283 int result;
61284 int whence;
61285
61286 MA_ASSERT(file != NULL);
61287
61288 (void)pVFS;
61289
61290 if (origin == ma_seek_origin_start) {
61291 whence = SEEK_SET;
61292 } else if (origin == ma_seek_origin_end) {
61293 whence = SEEK_END;
61294 } else {
61295 whence = SEEK_CUR;
61296 }
61297
61298#if defined(_WIN32)
61299 #if defined(_MSC_VER) && _MSC_VER > 1200
61300 result = _fseeki64((FILE*)file, offset, whence);
61301 #else
61302 /* No _fseeki64() so restrict to 31 bits. */
61303 if (offset > 0x7FFFFFFF) {
61304 return MA_OUT_OF_RANGE;
61305 }
61306
61307 result = fseek((FILE*)file, (int)offset, whence);
61308 #endif
61309#else
61310 result = fseek((FILE*)file, (long int)offset, whence);
61311#endif
61312 if (result != 0) {
61313 return MA_ERROR;
61314 }
61315
61316 return MA_SUCCESS;
61317}
61318
61319static ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
61320{
61321 ma_int64 result;
61322
61323 MA_ASSERT(file != NULL);
61324 MA_ASSERT(pCursor != NULL);
61325
61326 (void)pVFS;
61327
61328#if defined(_WIN32)
61329 #if defined(_MSC_VER) && _MSC_VER > 1200
61330 result = _ftelli64((FILE*)file);
61331 #else
61332 result = ftell((FILE*)file);
61333 #endif
61334#else
61335 result = ftell((FILE*)file);
61336#endif
61337
61338 *pCursor = result;
61339
61340 return MA_SUCCESS;
61341}
61342
61343#if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD)
61344int fileno(FILE *stream);
61345#endif
61346
61347static ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
61348{
61349 int fd;
61350 struct stat info;
61351
61352 MA_ASSERT(file != NULL);
61353 MA_ASSERT(pInfo != NULL);
61354
61355 (void)pVFS;
61356
61357#if defined(_MSC_VER)
61358 fd = _fileno((FILE*)file);
61359#else
61360 fd = fileno((FILE*)file);
61361#endif
61362
61363 if (fstat(fd, &info) != 0) {
61364 return ma_result_from_errno(errno);
61365 }
61366
61367 pInfo->sizeInBytes = info.st_size;
61368
61369 return MA_SUCCESS;
61370}
61371#endif
61372
61373
61374static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
61375{
61376 if (pFile == NULL) {
61377 return MA_INVALID_ARGS;
61378 }
61379
61380 *pFile = NULL;
61381
61382 if (pFilePath == NULL || openMode == 0) {
61383 return MA_INVALID_ARGS;
61384 }
61385
61386#if defined(MA_USE_WIN32_FILEIO)
61387 return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile);
61388#else
61389 return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile);
61390#endif
61391}
61392
61393static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
61394{
61395 if (pFile == NULL) {
61396 return MA_INVALID_ARGS;
61397 }
61398
61399 *pFile = NULL;
61400
61401 if (pFilePath == NULL || openMode == 0) {
61402 return MA_INVALID_ARGS;
61403 }
61404
61405#if defined(MA_USE_WIN32_FILEIO)
61406 return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile);
61407#else
61408 return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile);
61409#endif
61410}
61411
61412static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file)
61413{
61414 if (file == NULL) {
61415 return MA_INVALID_ARGS;
61416 }
61417
61418#if defined(MA_USE_WIN32_FILEIO)
61419 return ma_default_vfs_close__win32(pVFS, file);
61420#else
61421 return ma_default_vfs_close__stdio(pVFS, file);
61422#endif
61423}
61424
61425static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
61426{
61427 if (pBytesRead != NULL) {
61428 *pBytesRead = 0;
61429 }
61430
61431 if (file == NULL || pDst == NULL) {
61432 return MA_INVALID_ARGS;
61433 }
61434
61435#if defined(MA_USE_WIN32_FILEIO)
61436 return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead);
61437#else
61438 return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead);
61439#endif
61440}
61441
61442static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
61443{
61444 if (pBytesWritten != NULL) {
61445 *pBytesWritten = 0;
61446 }
61447
61448 if (file == NULL || pSrc == NULL) {
61449 return MA_INVALID_ARGS;
61450 }
61451
61452#if defined(MA_USE_WIN32_FILEIO)
61453 return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
61454#else
61455 return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
61456#endif
61457}
61458
61459static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
61460{
61461 if (file == NULL) {
61462 return MA_INVALID_ARGS;
61463 }
61464
61465#if defined(MA_USE_WIN32_FILEIO)
61466 return ma_default_vfs_seek__win32(pVFS, file, offset, origin);
61467#else
61468 return ma_default_vfs_seek__stdio(pVFS, file, offset, origin);
61469#endif
61470}
61471
61472static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
61473{
61474 if (pCursor == NULL) {
61475 return MA_INVALID_ARGS;
61476 }
61477
61478 *pCursor = 0;
61479
61480 if (file == NULL) {
61481 return MA_INVALID_ARGS;
61482 }
61483
61484#if defined(MA_USE_WIN32_FILEIO)
61485 return ma_default_vfs_tell__win32(pVFS, file, pCursor);
61486#else
61487 return ma_default_vfs_tell__stdio(pVFS, file, pCursor);
61488#endif
61489}
61490
61491static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
61492{
61493 if (pInfo == NULL) {
61494 return MA_INVALID_ARGS;
61495 }
61496
61497 MA_ZERO_OBJECT(pInfo);
61498
61499 if (file == NULL) {
61500 return MA_INVALID_ARGS;
61501 }
61502
61503#if defined(MA_USE_WIN32_FILEIO)
61504 return ma_default_vfs_info__win32(pVFS, file, pInfo);
61505#else
61506 return ma_default_vfs_info__stdio(pVFS, file, pInfo);
61507#endif
61508}
61509
61510
61511MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks)
61512{
61513 if (pVFS == NULL) {
61514 return MA_INVALID_ARGS;
61515 }
61516
61517 pVFS->cb.onOpen = ma_default_vfs_open;
61518 pVFS->cb.onOpenW = ma_default_vfs_open_w;
61519 pVFS->cb.onClose = ma_default_vfs_close;
61520 pVFS->cb.onRead = ma_default_vfs_read;
61521 pVFS->cb.onWrite = ma_default_vfs_write;
61522 pVFS->cb.onSeek = ma_default_vfs_seek;
61523 pVFS->cb.onTell = ma_default_vfs_tell;
61524 pVFS->cb.onInfo = ma_default_vfs_info;
61525 ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks);
61526
61527 return MA_SUCCESS;
61528}
61529
61530
61531MA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
61532{
61533 if (pVFS != NULL) {
61534 return ma_vfs_open(pVFS, pFilePath, openMode, pFile);
61535 } else {
61536 return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile);
61537 }
61538}
61539
61540MA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
61541{
61542 if (pVFS != NULL) {
61543 return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile);
61544 } else {
61545 return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile);
61546 }
61547}
61548
61549MA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file)
61550{
61551 if (pVFS != NULL) {
61552 return ma_vfs_close(pVFS, file);
61553 } else {
61554 return ma_default_vfs_close(pVFS, file);
61555 }
61556}
61557
61558MA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
61559{
61560 if (pVFS != NULL) {
61561 return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead);
61562 } else {
61563 return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead);
61564 }
61565}
61566
61567MA_API ma_result ma_vfs_or_default_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
61568{
61569 if (pVFS != NULL) {
61570 return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
61571 } else {
61572 return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
61573 }
61574}
61575
61576MA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
61577{
61578 if (pVFS != NULL) {
61579 return ma_vfs_seek(pVFS, file, offset, origin);
61580 } else {
61581 return ma_default_vfs_seek(pVFS, file, offset, origin);
61582 }
61583}
61584
61585MA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
61586{
61587 if (pVFS != NULL) {
61588 return ma_vfs_tell(pVFS, file, pCursor);
61589 } else {
61590 return ma_default_vfs_tell(pVFS, file, pCursor);
61591 }
61592}
61593
61594MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
61595{
61596 if (pVFS != NULL) {
61597 return ma_vfs_info(pVFS, file, pInfo);
61598 } else {
61599 return ma_default_vfs_info(pVFS, file, pInfo);
61600 }
61601}
61602
61603
61604
61605static 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)
61606{
61607 ma_result result;
61608 ma_vfs_file file;
61609 ma_file_info info;
61610 void* pData;
61611 size_t bytesRead;
61612
61613 if (ppData != NULL) {
61614 *ppData = NULL;
61615 }
61616 if (pSize != NULL) {
61617 *pSize = 0;
61618 }
61619
61620 if (ppData == NULL) {
61621 return MA_INVALID_ARGS;
61622 }
61623
61624 if (pFilePath != NULL) {
61625 result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
61626 } else {
61627 result = ma_vfs_or_default_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file);
61628 }
61629 if (result != MA_SUCCESS) {
61630 return result;
61631 }
61632
61633 result = ma_vfs_or_default_info(pVFS, file, &info);
61634 if (result != MA_SUCCESS) {
61635 ma_vfs_or_default_close(pVFS, file);
61636 return result;
61637 }
61638
61639 if (info.sizeInBytes > MA_SIZE_MAX) {
61640 ma_vfs_or_default_close(pVFS, file);
61641 return MA_TOO_BIG;
61642 }
61643
61644 pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */
61645 if (pData == NULL) {
61646 ma_vfs_or_default_close(pVFS, file);
61647 return result;
61648 }
61649
61650 result = ma_vfs_or_default_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */
61651 ma_vfs_or_default_close(pVFS, file);
61652
61653 if (result != MA_SUCCESS) {
61654 ma_free(pData, pAllocationCallbacks);
61655 return result;
61656 }
61657
61658 if (pSize != NULL) {
61659 *pSize = bytesRead;
61660 }
61661
61662 MA_ASSERT(ppData != NULL);
61663 *ppData = pData;
61664
61665 return MA_SUCCESS;
61666}
61667
61668MA_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)
61669{
61670 return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks);
61671}
61672
61673MA_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)
61674{
61675 return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks);
61676}
61677
61678
61679
61680/**************************************************************************************************************************************************************
61681
61682Decoding and Encoding Headers. These are auto-generated from a tool.
61683
61684**************************************************************************************************************************************************************/
61685#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
61686/* dr_wav_h begin */
61687#ifndef ma_dr_wav_h
61688#define ma_dr_wav_h
61689#ifdef __cplusplus
61690extern "C" {
61691#endif
61692#define MA_DR_WAV_STRINGIFY(x) #x
61693#define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x)
61694#define MA_DR_WAV_VERSION_MAJOR 0
61695#define MA_DR_WAV_VERSION_MINOR 14
61696#define MA_DR_WAV_VERSION_REVISION 0
61697#define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION)
61698#include <stddef.h>
61699#define MA_DR_WAVE_FORMAT_PCM 0x1
61700#define MA_DR_WAVE_FORMAT_ADPCM 0x2
61701#define MA_DR_WAVE_FORMAT_IEEE_FLOAT 0x3
61702#define MA_DR_WAVE_FORMAT_ALAW 0x6
61703#define MA_DR_WAVE_FORMAT_MULAW 0x7
61704#define MA_DR_WAVE_FORMAT_DVI_ADPCM 0x11
61705#define MA_DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE
61706#define MA_DR_WAV_SEQUENTIAL 0x00000001
61707#define MA_DR_WAV_WITH_METADATA 0x00000002
61708MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);
61709MA_API const char* ma_dr_wav_version_string(void);
61710typedef enum
61711{
61712 MA_DR_WAV_SEEK_SET,
61713 MA_DR_WAV_SEEK_CUR,
61714 MA_DR_WAV_SEEK_END
61715} ma_dr_wav_seek_origin;
61716typedef enum
61717{
61718 ma_dr_wav_container_riff,
61719 ma_dr_wav_container_rifx,
61720 ma_dr_wav_container_w64,
61721 ma_dr_wav_container_rf64,
61722 ma_dr_wav_container_aiff
61723} ma_dr_wav_container;
61724typedef struct
61725{
61726 union
61727 {
61728 ma_uint8 fourcc[4];
61729 ma_uint8 guid[16];
61730 } id;
61731 ma_uint64 sizeInBytes;
61732 unsigned int paddingSize;
61733} ma_dr_wav_chunk_header;
61734typedef struct
61735{
61736 ma_uint16 formatTag;
61737 ma_uint16 channels;
61738 ma_uint32 sampleRate;
61739 ma_uint32 avgBytesPerSec;
61740 ma_uint16 blockAlign;
61741 ma_uint16 bitsPerSample;
61742 ma_uint16 extendedSize;
61743 ma_uint16 validBitsPerSample;
61744 ma_uint32 channelMask;
61745 ma_uint8 subFormat[16];
61746} ma_dr_wav_fmt;
61747MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT);
61748typedef size_t (* ma_dr_wav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
61749typedef size_t (* ma_dr_wav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);
61750typedef ma_bool32 (* ma_dr_wav_seek_proc)(void* pUserData, int offset, ma_dr_wav_seek_origin origin);
61751typedef ma_bool32 (* ma_dr_wav_tell_proc)(void* pUserData, ma_int64* pCursor);
61752typedef ma_uint64 (* ma_dr_wav_chunk_proc)(void* pChunkUserData, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_container container, const ma_dr_wav_fmt* pFMT);
61753typedef struct
61754{
61755 const ma_uint8* data;
61756 size_t dataSize;
61757 size_t currentReadPos;
61758} ma_dr_wav__memory_stream;
61759typedef struct
61760{
61761 void** ppData;
61762 size_t* pDataSize;
61763 size_t dataSize;
61764 size_t dataCapacity;
61765 size_t currentWritePos;
61766} ma_dr_wav__memory_stream_write;
61767typedef struct
61768{
61769 ma_dr_wav_container container;
61770 ma_uint32 format;
61771 ma_uint32 channels;
61772 ma_uint32 sampleRate;
61773 ma_uint32 bitsPerSample;
61774} ma_dr_wav_data_format;
61775typedef enum
61776{
61777 ma_dr_wav_metadata_type_none = 0,
61778 ma_dr_wav_metadata_type_unknown = 1 << 0,
61779 ma_dr_wav_metadata_type_smpl = 1 << 1,
61780 ma_dr_wav_metadata_type_inst = 1 << 2,
61781 ma_dr_wav_metadata_type_cue = 1 << 3,
61782 ma_dr_wav_metadata_type_acid = 1 << 4,
61783 ma_dr_wav_metadata_type_bext = 1 << 5,
61784 ma_dr_wav_metadata_type_list_label = 1 << 6,
61785 ma_dr_wav_metadata_type_list_note = 1 << 7,
61786 ma_dr_wav_metadata_type_list_labelled_cue_region = 1 << 8,
61787 ma_dr_wav_metadata_type_list_info_software = 1 << 9,
61788 ma_dr_wav_metadata_type_list_info_copyright = 1 << 10,
61789 ma_dr_wav_metadata_type_list_info_title = 1 << 11,
61790 ma_dr_wav_metadata_type_list_info_artist = 1 << 12,
61791 ma_dr_wav_metadata_type_list_info_comment = 1 << 13,
61792 ma_dr_wav_metadata_type_list_info_date = 1 << 14,
61793 ma_dr_wav_metadata_type_list_info_genre = 1 << 15,
61794 ma_dr_wav_metadata_type_list_info_album = 1 << 16,
61795 ma_dr_wav_metadata_type_list_info_tracknumber = 1 << 17,
61796 ma_dr_wav_metadata_type_list_info_location = 1 << 18,
61797 ma_dr_wav_metadata_type_list_info_organization = 1 << 19,
61798 ma_dr_wav_metadata_type_list_info_keywords = 1 << 20,
61799 ma_dr_wav_metadata_type_list_info_medium = 1 << 21,
61800 ma_dr_wav_metadata_type_list_info_description = 1 << 22,
61801 ma_dr_wav_metadata_type_list_all_info_strings = ma_dr_wav_metadata_type_list_info_software
61802 | ma_dr_wav_metadata_type_list_info_copyright
61803 | ma_dr_wav_metadata_type_list_info_title
61804 | ma_dr_wav_metadata_type_list_info_artist
61805 | ma_dr_wav_metadata_type_list_info_comment
61806 | ma_dr_wav_metadata_type_list_info_date
61807 | ma_dr_wav_metadata_type_list_info_genre
61808 | ma_dr_wav_metadata_type_list_info_album
61809 | ma_dr_wav_metadata_type_list_info_tracknumber
61810 | ma_dr_wav_metadata_type_list_info_location
61811 | ma_dr_wav_metadata_type_list_info_organization
61812 | ma_dr_wav_metadata_type_list_info_keywords
61813 | ma_dr_wav_metadata_type_list_info_medium
61814 | ma_dr_wav_metadata_type_list_info_description,
61815 ma_dr_wav_metadata_type_list_all_adtl = ma_dr_wav_metadata_type_list_label
61816 | ma_dr_wav_metadata_type_list_note
61817 | ma_dr_wav_metadata_type_list_labelled_cue_region,
61818 ma_dr_wav_metadata_type_all = -2,
61819 ma_dr_wav_metadata_type_all_including_unknown = -1
61820} ma_dr_wav_metadata_type;
61821typedef enum
61822{
61823 ma_dr_wav_smpl_loop_type_forward = 0,
61824 ma_dr_wav_smpl_loop_type_pingpong = 1,
61825 ma_dr_wav_smpl_loop_type_backward = 2
61826} ma_dr_wav_smpl_loop_type;
61827typedef struct
61828{
61829 ma_uint32 cuePointId;
61830 ma_uint32 type;
61831 ma_uint32 firstSampleOffset;
61832 ma_uint32 lastSampleOffset;
61833 ma_uint32 sampleFraction;
61834 ma_uint32 playCount;
61835} ma_dr_wav_smpl_loop;
61836typedef struct
61837{
61838 ma_uint32 manufacturerId;
61839 ma_uint32 productId;
61840 ma_uint32 samplePeriodNanoseconds;
61841 ma_uint32 midiUnityNote;
61842 ma_uint32 midiPitchFraction;
61843 ma_uint32 smpteFormat;
61844 ma_uint32 smpteOffset;
61845 ma_uint32 sampleLoopCount;
61846 ma_uint32 samplerSpecificDataSizeInBytes;
61847 ma_dr_wav_smpl_loop* pLoops;
61848 ma_uint8* pSamplerSpecificData;
61849} ma_dr_wav_smpl;
61850typedef struct
61851{
61852 ma_int8 midiUnityNote;
61853 ma_int8 fineTuneCents;
61854 ma_int8 gainDecibels;
61855 ma_int8 lowNote;
61856 ma_int8 highNote;
61857 ma_int8 lowVelocity;
61858 ma_int8 highVelocity;
61859} ma_dr_wav_inst;
61860typedef struct
61861{
61862 ma_uint32 id;
61863 ma_uint32 playOrderPosition;
61864 ma_uint8 dataChunkId[4];
61865 ma_uint32 chunkStart;
61866 ma_uint32 blockStart;
61867 ma_uint32 sampleOffset;
61868} ma_dr_wav_cue_point;
61869typedef struct
61870{
61871 ma_uint32 cuePointCount;
61872 ma_dr_wav_cue_point *pCuePoints;
61873} ma_dr_wav_cue;
61874typedef enum
61875{
61876 ma_dr_wav_acid_flag_one_shot = 1,
61877 ma_dr_wav_acid_flag_root_note_set = 2,
61878 ma_dr_wav_acid_flag_stretch = 4,
61879 ma_dr_wav_acid_flag_disk_based = 8,
61880 ma_dr_wav_acid_flag_acidizer = 16
61881} ma_dr_wav_acid_flag;
61882typedef struct
61883{
61884 ma_uint32 flags;
61885 ma_uint16 midiUnityNote;
61886 ma_uint16 reserved1;
61887 float reserved2;
61888 ma_uint32 numBeats;
61889 ma_uint16 meterDenominator;
61890 ma_uint16 meterNumerator;
61891 float tempo;
61892} ma_dr_wav_acid;
61893typedef struct
61894{
61895 ma_uint32 cuePointId;
61896 ma_uint32 stringLength;
61897 char* pString;
61898} ma_dr_wav_list_label_or_note;
61899typedef struct
61900{
61901 char* pDescription;
61902 char* pOriginatorName;
61903 char* pOriginatorReference;
61904 char pOriginationDate[10];
61905 char pOriginationTime[8];
61906 ma_uint64 timeReference;
61907 ma_uint16 version;
61908 char* pCodingHistory;
61909 ma_uint32 codingHistorySize;
61910 ma_uint8* pUMID;
61911 ma_uint16 loudnessValue;
61912 ma_uint16 loudnessRange;
61913 ma_uint16 maxTruePeakLevel;
61914 ma_uint16 maxMomentaryLoudness;
61915 ma_uint16 maxShortTermLoudness;
61916} ma_dr_wav_bext;
61917typedef struct
61918{
61919 ma_uint32 stringLength;
61920 char* pString;
61921} ma_dr_wav_list_info_text;
61922typedef struct
61923{
61924 ma_uint32 cuePointId;
61925 ma_uint32 sampleLength;
61926 ma_uint8 purposeId[4];
61927 ma_uint16 country;
61928 ma_uint16 language;
61929 ma_uint16 dialect;
61930 ma_uint16 codePage;
61931 ma_uint32 stringLength;
61932 char* pString;
61933} ma_dr_wav_list_labelled_cue_region;
61934typedef enum
61935{
61936 ma_dr_wav_metadata_location_invalid,
61937 ma_dr_wav_metadata_location_top_level,
61938 ma_dr_wav_metadata_location_inside_info_list,
61939 ma_dr_wav_metadata_location_inside_adtl_list
61940} ma_dr_wav_metadata_location;
61941typedef struct
61942{
61943 ma_uint8 id[4];
61944 ma_dr_wav_metadata_location chunkLocation;
61945 ma_uint32 dataSizeInBytes;
61946 ma_uint8* pData;
61947} ma_dr_wav_unknown_metadata;
61948typedef struct
61949{
61950 ma_dr_wav_metadata_type type;
61951 union
61952 {
61953 ma_dr_wav_cue cue;
61954 ma_dr_wav_smpl smpl;
61955 ma_dr_wav_acid acid;
61956 ma_dr_wav_inst inst;
61957 ma_dr_wav_bext bext;
61958 ma_dr_wav_list_label_or_note labelOrNote;
61959 ma_dr_wav_list_labelled_cue_region labelledCueRegion;
61960 ma_dr_wav_list_info_text infoText;
61961 ma_dr_wav_unknown_metadata unknown;
61962 } data;
61963} ma_dr_wav_metadata;
61964typedef struct
61965{
61966 ma_dr_wav_read_proc onRead;
61967 ma_dr_wav_write_proc onWrite;
61968 ma_dr_wav_seek_proc onSeek;
61969 ma_dr_wav_tell_proc onTell;
61970 void* pUserData;
61971 ma_allocation_callbacks allocationCallbacks;
61972 ma_dr_wav_container container;
61973 ma_dr_wav_fmt fmt;
61974 ma_uint32 sampleRate;
61975 ma_uint16 channels;
61976 ma_uint16 bitsPerSample;
61977 ma_uint16 translatedFormatTag;
61978 ma_uint64 totalPCMFrameCount;
61979 ma_uint64 dataChunkDataSize;
61980 ma_uint64 dataChunkDataPos;
61981 ma_uint64 bytesRemaining;
61982 ma_uint64 readCursorInPCMFrames;
61983 ma_uint64 dataChunkDataSizeTargetWrite;
61984 ma_bool32 isSequentialWrite;
61985 ma_dr_wav_metadata* pMetadata;
61986 ma_uint32 metadataCount;
61987 ma_dr_wav__memory_stream memoryStream;
61988 ma_dr_wav__memory_stream_write memoryStreamWrite;
61989 struct
61990 {
61991 ma_uint32 bytesRemainingInBlock;
61992 ma_uint16 predictor[2];
61993 ma_int32 delta[2];
61994 ma_int32 cachedFrames[4];
61995 ma_uint32 cachedFrameCount;
61996 ma_int32 prevFrames[2][2];
61997 } msadpcm;
61998 struct
61999 {
62000 ma_uint32 bytesRemainingInBlock;
62001 ma_int32 predictor[2];
62002 ma_int32 stepIndex[2];
62003 ma_int32 cachedFrames[16];
62004 ma_uint32 cachedFrameCount;
62005 } ima;
62006 struct
62007 {
62008 ma_bool8 isLE;
62009 ma_bool8 isUnsigned;
62010 } aiff;
62011} ma_dr_wav;
62012MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
62013MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, ma_dr_wav_chunk_proc onChunk, void* pReadSeekTellUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);
62014MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);
62015MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
62016MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
62017MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
62018MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount);
62019MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount);
62020MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav);
62021MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav);
62022MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut);
62023MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut);
62024MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut);
62025MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut);
62026MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex);
62027MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor);
62028MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength);
62029MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData);
62030MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData);
62031MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData);
62032MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData);
62033#ifndef MA_DR_WAV_NO_CONVERSION_API
62034MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut);
62035MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut);
62036MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut);
62037MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount);
62038MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount);
62039MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount);
62040MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount);
62041MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount);
62042MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount);
62043MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount);
62044MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut);
62045MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut);
62046MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut);
62047MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount);
62048MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount);
62049MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount);
62050MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount);
62051MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount);
62052MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount);
62053MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount);
62054MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut);
62055MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut);
62056MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut);
62057MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount);
62058MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount);
62059MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount);
62060MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount);
62061MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount);
62062MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount);
62063MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount);
62064#endif
62065#ifndef MA_DR_WAV_NO_STDIO
62066MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks);
62067MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);
62068MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks);
62069MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);
62070MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);
62071MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);
62072MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks);
62073MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks);
62074MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62075MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks);
62076MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks);
62077MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62078#endif
62079MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks);
62080MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);
62081MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);
62082MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks);
62083MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks);
62084MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62085#ifndef MA_DR_WAV_NO_CONVERSION_API
62086MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
62087MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
62088MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
62089#ifndef MA_DR_WAV_NO_STDIO
62090MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
62091MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
62092MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
62093MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
62094MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
62095MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
62096#endif
62097MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
62098MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
62099MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
62100#endif
62101MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
62102MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data);
62103MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data);
62104MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data);
62105MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data);
62106MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data);
62107MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data);
62108MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data);
62109MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]);
62110MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b);
62111#ifdef __cplusplus
62112}
62113#endif
62114#endif
62115/* dr_wav_h end */
62116#endif /* MA_NO_WAV */
62117
62118#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
62119/* dr_flac_h begin */
62120#ifndef ma_dr_flac_h
62121#define ma_dr_flac_h
62122#ifdef __cplusplus
62123extern "C" {
62124#endif
62125#define MA_DR_FLAC_STRINGIFY(x) #x
62126#define MA_DR_FLAC_XSTRINGIFY(x) MA_DR_FLAC_STRINGIFY(x)
62127#define MA_DR_FLAC_VERSION_MAJOR 0
62128#define MA_DR_FLAC_VERSION_MINOR 13
62129#define MA_DR_FLAC_VERSION_REVISION 0
62130#define MA_DR_FLAC_VERSION_STRING MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION)
62131#include <stddef.h>
62132#if defined(_MSC_VER) && _MSC_VER >= 1700
62133 #define MA_DR_FLAC_DEPRECATED __declspec(deprecated)
62134#elif (defined(__GNUC__) && __GNUC__ >= 4)
62135 #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated))
62136#elif defined(__has_feature)
62137 #if __has_feature(attribute_deprecated)
62138 #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated))
62139 #else
62140 #define MA_DR_FLAC_DEPRECATED
62141 #endif
62142#else
62143 #define MA_DR_FLAC_DEPRECATED
62144#endif
62145MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);
62146MA_API const char* ma_dr_flac_version_string(void);
62147#ifndef MA_DR_FLAC_BUFFER_SIZE
62148#define MA_DR_FLAC_BUFFER_SIZE 4096
62149#endif
62150#ifdef MA_64BIT
62151typedef ma_uint64 ma_dr_flac_cache_t;
62152#else
62153typedef ma_uint32 ma_dr_flac_cache_t;
62154#endif
62155#define MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO 0
62156#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING 1
62157#define MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION 2
62158#define MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3
62159#define MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4
62160#define MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET 5
62161#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE 6
62162#define MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID 127
62163#define MA_DR_FLAC_PICTURE_TYPE_OTHER 0
62164#define MA_DR_FLAC_PICTURE_TYPE_FILE_ICON 1
62165#define MA_DR_FLAC_PICTURE_TYPE_OTHER_FILE_ICON 2
62166#define MA_DR_FLAC_PICTURE_TYPE_COVER_FRONT 3
62167#define MA_DR_FLAC_PICTURE_TYPE_COVER_BACK 4
62168#define MA_DR_FLAC_PICTURE_TYPE_LEAFLET_PAGE 5
62169#define MA_DR_FLAC_PICTURE_TYPE_MEDIA 6
62170#define MA_DR_FLAC_PICTURE_TYPE_LEAD_ARTIST 7
62171#define MA_DR_FLAC_PICTURE_TYPE_ARTIST 8
62172#define MA_DR_FLAC_PICTURE_TYPE_CONDUCTOR 9
62173#define MA_DR_FLAC_PICTURE_TYPE_BAND 10
62174#define MA_DR_FLAC_PICTURE_TYPE_COMPOSER 11
62175#define MA_DR_FLAC_PICTURE_TYPE_LYRICIST 12
62176#define MA_DR_FLAC_PICTURE_TYPE_RECORDING_LOCATION 13
62177#define MA_DR_FLAC_PICTURE_TYPE_DURING_RECORDING 14
62178#define MA_DR_FLAC_PICTURE_TYPE_DURING_PERFORMANCE 15
62179#define MA_DR_FLAC_PICTURE_TYPE_SCREEN_CAPTURE 16
62180#define MA_DR_FLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17
62181#define MA_DR_FLAC_PICTURE_TYPE_ILLUSTRATION 18
62182#define MA_DR_FLAC_PICTURE_TYPE_BAND_LOGOTYPE 19
62183#define MA_DR_FLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20
62184typedef enum
62185{
62186 ma_dr_flac_container_native,
62187 ma_dr_flac_container_ogg,
62188 ma_dr_flac_container_unknown
62189} ma_dr_flac_container;
62190typedef enum
62191{
62192 MA_DR_FLAC_SEEK_SET,
62193 MA_DR_FLAC_SEEK_CUR,
62194 MA_DR_FLAC_SEEK_END
62195} ma_dr_flac_seek_origin;
62196typedef struct
62197{
62198 ma_uint64 firstPCMFrame;
62199 ma_uint64 flacFrameOffset;
62200 ma_uint16 pcmFrameCount;
62201} ma_dr_flac_seekpoint;
62202typedef struct
62203{
62204 ma_uint16 minBlockSizeInPCMFrames;
62205 ma_uint16 maxBlockSizeInPCMFrames;
62206 ma_uint32 minFrameSizeInPCMFrames;
62207 ma_uint32 maxFrameSizeInPCMFrames;
62208 ma_uint32 sampleRate;
62209 ma_uint8 channels;
62210 ma_uint8 bitsPerSample;
62211 ma_uint64 totalPCMFrameCount;
62212 ma_uint8 md5[16];
62213} ma_dr_flac_streaminfo;
62214typedef struct
62215{
62216 ma_uint32 type;
62217 const void* pRawData;
62218 ma_uint32 rawDataSize;
62219 union
62220 {
62221 ma_dr_flac_streaminfo streaminfo;
62222 struct
62223 {
62224 int unused;
62225 } padding;
62226 struct
62227 {
62228 ma_uint32 id;
62229 const void* pData;
62230 ma_uint32 dataSize;
62231 } application;
62232 struct
62233 {
62234 ma_uint32 seekpointCount;
62235 const ma_dr_flac_seekpoint* pSeekpoints;
62236 } seektable;
62237 struct
62238 {
62239 ma_uint32 vendorLength;
62240 const char* vendor;
62241 ma_uint32 commentCount;
62242 const void* pComments;
62243 } vorbis_comment;
62244 struct
62245 {
62246 char catalog[128];
62247 ma_uint64 leadInSampleCount;
62248 ma_bool32 isCD;
62249 ma_uint8 trackCount;
62250 const void* pTrackData;
62251 } cuesheet;
62252 struct
62253 {
62254 ma_uint32 type;
62255 ma_uint32 mimeLength;
62256 const char* mime;
62257 ma_uint32 descriptionLength;
62258 const char* description;
62259 ma_uint32 width;
62260 ma_uint32 height;
62261 ma_uint32 colorDepth;
62262 ma_uint32 indexColorCount;
62263 ma_uint32 pictureDataSize;
62264 const ma_uint8* pPictureData;
62265 } picture;
62266 } data;
62267} ma_dr_flac_metadata;
62268typedef size_t (* ma_dr_flac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
62269typedef ma_bool32 (* ma_dr_flac_seek_proc)(void* pUserData, int offset, ma_dr_flac_seek_origin origin);
62270typedef ma_bool32 (* ma_dr_flac_tell_proc)(void* pUserData, ma_int64* pCursor);
62271typedef void (* ma_dr_flac_meta_proc)(void* pUserData, ma_dr_flac_metadata* pMetadata);
62272typedef struct
62273{
62274 const ma_uint8* data;
62275 size_t dataSize;
62276 size_t currentReadPos;
62277} ma_dr_flac__memory_stream;
62278typedef struct
62279{
62280 ma_dr_flac_read_proc onRead;
62281 ma_dr_flac_seek_proc onSeek;
62282 ma_dr_flac_tell_proc onTell;
62283 void* pUserData;
62284 size_t unalignedByteCount;
62285 ma_dr_flac_cache_t unalignedCache;
62286 ma_uint32 nextL2Line;
62287 ma_uint32 consumedBits;
62288 ma_dr_flac_cache_t cacheL2[MA_DR_FLAC_BUFFER_SIZE/sizeof(ma_dr_flac_cache_t)];
62289 ma_dr_flac_cache_t cache;
62290 ma_uint16 crc16;
62291 ma_dr_flac_cache_t crc16Cache;
62292 ma_uint32 crc16CacheIgnoredBytes;
62293} ma_dr_flac_bs;
62294typedef struct
62295{
62296 ma_uint8 subframeType;
62297 ma_uint8 wastedBitsPerSample;
62298 ma_uint8 lpcOrder;
62299 ma_int32* pSamplesS32;
62300} ma_dr_flac_subframe;
62301typedef struct
62302{
62303 ma_uint64 pcmFrameNumber;
62304 ma_uint32 flacFrameNumber;
62305 ma_uint32 sampleRate;
62306 ma_uint16 blockSizeInPCMFrames;
62307 ma_uint8 channelAssignment;
62308 ma_uint8 bitsPerSample;
62309 ma_uint8 crc8;
62310} ma_dr_flac_frame_header;
62311typedef struct
62312{
62313 ma_dr_flac_frame_header header;
62314 ma_uint32 pcmFramesRemaining;
62315 ma_dr_flac_subframe subframes[8];
62316} ma_dr_flac_frame;
62317typedef struct
62318{
62319 ma_dr_flac_meta_proc onMeta;
62320 void* pUserDataMD;
62321 ma_allocation_callbacks allocationCallbacks;
62322 ma_uint32 sampleRate;
62323 ma_uint8 channels;
62324 ma_uint8 bitsPerSample;
62325 ma_uint16 maxBlockSizeInPCMFrames;
62326 ma_uint64 totalPCMFrameCount;
62327 ma_dr_flac_container container;
62328 ma_uint32 seekpointCount;
62329 ma_dr_flac_frame currentFLACFrame;
62330 ma_uint64 currentPCMFrame;
62331 ma_uint64 firstFLACFramePosInBytes;
62332 ma_dr_flac__memory_stream memoryStream;
62333 ma_int32* pDecodedSamples;
62334 ma_dr_flac_seekpoint* pSeekpoints;
62335 void* _oggbs;
62336 ma_bool32 _noSeekTableSeek : 1;
62337 ma_bool32 _noBinarySearchSeek : 1;
62338 ma_bool32 _noBruteForceSeek : 1;
62339 ma_dr_flac_bs bs;
62340 ma_uint8 pExtraData[1];
62341} ma_dr_flac;
62342MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
62343MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
62344MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
62345MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
62346MA_API void ma_dr_flac_close(ma_dr_flac* pFlac);
62347MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut);
62348MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut);
62349MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut);
62350MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex);
62351#ifndef MA_DR_FLAC_NO_STDIO
62352MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks);
62353MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks);
62354MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
62355MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
62356#endif
62357MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks);
62358MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
62359MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62360MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62361MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62362#ifndef MA_DR_FLAC_NO_STDIO
62363MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62364MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62365MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62366#endif
62367MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62368MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62369MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62370MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
62371typedef struct
62372{
62373 ma_uint32 countRemaining;
62374 const char* pRunningData;
62375} ma_dr_flac_vorbis_comment_iterator;
62376MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments);
62377MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut);
62378typedef struct
62379{
62380 ma_uint32 countRemaining;
62381 const char* pRunningData;
62382} ma_dr_flac_cuesheet_track_iterator;
62383typedef struct
62384{
62385 ma_uint64 offset;
62386 ma_uint8 index;
62387 ma_uint8 reserved[3];
62388} ma_dr_flac_cuesheet_track_index;
62389typedef struct
62390{
62391 ma_uint64 offset;
62392 ma_uint8 trackNumber;
62393 char ISRC[12];
62394 ma_bool8 isAudio;
62395 ma_bool8 preEmphasis;
62396 ma_uint8 indexCount;
62397 const ma_dr_flac_cuesheet_track_index* pIndexPoints;
62398} ma_dr_flac_cuesheet_track;
62399MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData);
62400MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack);
62401#ifdef __cplusplus
62402}
62403#endif
62404#endif
62405/* dr_flac_h end */
62406#endif /* MA_NO_FLAC */
62407
62408#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
62409#ifndef MA_DR_MP3_NO_SIMD
62410 #if (defined(MA_NO_NEON) && defined(MA_ARM)) || (defined(MA_NO_SSE2) && (defined(MA_X86) || defined(MA_X64)))
62411 #define MA_DR_MP3_NO_SIMD
62412 #endif
62413#endif
62414
62415/* dr_mp3_h begin */
62416#ifndef ma_dr_mp3_h
62417#define ma_dr_mp3_h
62418#ifdef __cplusplus
62419extern "C" {
62420#endif
62421#define MA_DR_MP3_STRINGIFY(x) #x
62422#define MA_DR_MP3_XSTRINGIFY(x) MA_DR_MP3_STRINGIFY(x)
62423#define MA_DR_MP3_VERSION_MAJOR 0
62424#define MA_DR_MP3_VERSION_MINOR 7
62425#define MA_DR_MP3_VERSION_REVISION 0
62426#define MA_DR_MP3_VERSION_STRING MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION)
62427#include <stddef.h>
62428#define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152
62429#define MA_DR_MP3_MAX_SAMPLES_PER_FRAME (MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2)
62430MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);
62431MA_API const char* ma_dr_mp3_version_string(void);
62432typedef struct
62433{
62434 int frame_bytes, channels, sample_rate, layer, bitrate_kbps;
62435} ma_dr_mp3dec_frame_info;
62436typedef struct
62437{
62438 float mdct_overlap[2][9*32], qmf_state[15*2*32];
62439 int reserv, free_format_bytes;
62440 ma_uint8 header[4], reserv_buf[511];
62441} ma_dr_mp3dec;
62442MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec);
62443MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info);
62444MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples);
62445typedef enum
62446{
62447 MA_DR_MP3_SEEK_SET,
62448 MA_DR_MP3_SEEK_CUR,
62449 MA_DR_MP3_SEEK_END
62450} ma_dr_mp3_seek_origin;
62451typedef struct
62452{
62453 ma_uint64 seekPosInBytes;
62454 ma_uint64 pcmFrameIndex;
62455 ma_uint16 mp3FramesToDiscard;
62456 ma_uint16 pcmFramesToDiscard;
62457} ma_dr_mp3_seek_point;
62458typedef enum
62459{
62460 MA_DR_MP3_METADATA_TYPE_ID3V1,
62461 MA_DR_MP3_METADATA_TYPE_ID3V2,
62462 MA_DR_MP3_METADATA_TYPE_APE,
62463 MA_DR_MP3_METADATA_TYPE_XING,
62464 MA_DR_MP3_METADATA_TYPE_VBRI
62465} ma_dr_mp3_metadata_type;
62466typedef struct
62467{
62468 ma_dr_mp3_metadata_type type;
62469 const void* pRawData;
62470 size_t rawDataSize;
62471} ma_dr_mp3_metadata;
62472typedef size_t (* ma_dr_mp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
62473typedef ma_bool32 (* ma_dr_mp3_seek_proc)(void* pUserData, int offset, ma_dr_mp3_seek_origin origin);
62474typedef ma_bool32 (* ma_dr_mp3_tell_proc)(void* pUserData, ma_int64* pCursor);
62475typedef void (* ma_dr_mp3_meta_proc)(void* pUserData, const ma_dr_mp3_metadata* pMetadata);
62476typedef struct
62477{
62478 ma_uint32 channels;
62479 ma_uint32 sampleRate;
62480} ma_dr_mp3_config;
62481typedef struct
62482{
62483 ma_dr_mp3dec decoder;
62484 ma_uint32 channels;
62485 ma_uint32 sampleRate;
62486 ma_dr_mp3_read_proc onRead;
62487 ma_dr_mp3_seek_proc onSeek;
62488 ma_dr_mp3_meta_proc onMeta;
62489 void* pUserData;
62490 void* pUserDataMeta;
62491 ma_allocation_callbacks allocationCallbacks;
62492 ma_uint32 mp3FrameChannels;
62493 ma_uint32 mp3FrameSampleRate;
62494 ma_uint32 pcmFramesConsumedInMP3Frame;
62495 ma_uint32 pcmFramesRemainingInMP3Frame;
62496 ma_uint8 pcmFrames[sizeof(float)*MA_DR_MP3_MAX_SAMPLES_PER_FRAME];
62497 ma_uint64 currentPCMFrame;
62498 ma_uint64 streamCursor;
62499 ma_uint64 streamLength;
62500 ma_uint64 streamStartOffset;
62501 ma_dr_mp3_seek_point* pSeekPoints;
62502 ma_uint32 seekPointCount;
62503 ma_uint32 delayInPCMFrames;
62504 ma_uint32 paddingInPCMFrames;
62505 ma_uint64 totalPCMFrameCount;
62506 ma_bool32 isVBR;
62507 ma_bool32 isCBR;
62508 size_t dataSize;
62509 size_t dataCapacity;
62510 size_t dataConsumed;
62511 ma_uint8* pData;
62512 ma_bool32 atEnd;
62513 struct
62514 {
62515 const ma_uint8* pData;
62516 size_t dataSize;
62517 size_t currentReadPos;
62518 } memory;
62519} ma_dr_mp3;
62520MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, ma_dr_mp3_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
62521MA_API ma_bool32 ma_dr_mp3_init_memory_with_metadata(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks);
62522MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks);
62523#ifndef MA_DR_MP3_NO_STDIO
62524MA_API ma_bool32 ma_dr_mp3_init_file_with_metadata(ma_dr_mp3* pMP3, const char* pFilePath, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks);
62525MA_API ma_bool32 ma_dr_mp3_init_file_with_metadata_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks);
62526MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks);
62527MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks);
62528#endif
62529MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3);
62530MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut);
62531MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut);
62532MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex);
62533MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3);
62534MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3);
62535MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount);
62536MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints);
62537MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints);
62538MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62539MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62540MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62541MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62542#ifndef MA_DR_MP3_NO_STDIO
62543MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62544MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
62545#endif
62546MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
62547MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
62548#ifdef __cplusplus
62549}
62550#endif
62551#endif
62552/* dr_mp3_h end */
62553#endif /* MA_NO_MP3 */
62554
62555
62556/**************************************************************************************************************************************************************
62557
62558Decoding
62559
62560**************************************************************************************************************************************************************/
62561#ifndef MA_NO_DECODING
62562
62563static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
62564{
62565 MA_ASSERT(pDecoder != NULL);
62566
62567 return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead, pBytesRead);
62568}
62569
62570static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin)
62571{
62572 MA_ASSERT(pDecoder != NULL);
62573
62574 return pDecoder->onSeek(pDecoder, byteOffset, origin);
62575}
62576
62577static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor)
62578{
62579 MA_ASSERT(pDecoder != NULL);
62580
62581 if (pDecoder->onTell == NULL) {
62582 return MA_NOT_IMPLEMENTED;
62583 }
62584
62585 return pDecoder->onTell(pDecoder, pCursor);
62586}
62587
62588
62589MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount)
62590{
62591 ma_decoding_backend_config config;
62592
62593 MA_ZERO_OBJECT(&config);
62594 config.preferredFormat = preferredFormat;
62595 config.seekPointCount = seekPointCount;
62596
62597 return config;
62598}
62599
62600
62601MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
62602{
62603 ma_decoder_config config;
62604 MA_ZERO_OBJECT(&config);
62605 config.format = outputFormat;
62606 config.channels = outputChannels;
62607 config.sampleRate = outputSampleRate;
62608 config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate doesn't matter here. */
62610
62611 /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */
62612
62613 return config;
62614}
62615
62616MA_API ma_decoder_config ma_decoder_config_init_default(void)
62617{
62619}
62620
62621MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig)
62622{
62623 ma_decoder_config config;
62624 if (pConfig != NULL) {
62625 config = *pConfig;
62626 } else {
62627 MA_ZERO_OBJECT(&config);
62628 }
62629
62630 return config;
62631}
62632
62633static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig)
62634{
62635 ma_result result;
62636 ma_data_converter_config converterConfig;
62637 ma_format internalFormat;
62638 ma_uint32 internalChannels;
62639 ma_uint32 internalSampleRate;
62640 ma_channel internalChannelMap[MA_MAX_CHANNELS];
62641
62642 MA_ASSERT(pDecoder != NULL);
62643 MA_ASSERT(pConfig != NULL);
62644
62645 result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap));
62646 if (result != MA_SUCCESS) {
62647 return result; /* Failed to retrieve the internal data format. */
62648 }
62649
62650
62651 /* Make sure we're not asking for too many channels. */
62652 if (pConfig->channels > MA_MAX_CHANNELS) {
62653 return MA_INVALID_ARGS;
62654 }
62655
62656 /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */
62657 if (internalChannels > MA_MAX_CHANNELS) {
62658 return MA_INVALID_ARGS;
62659 }
62660
62661
62662 /* Output format. */
62663 if (pConfig->format == ma_format_unknown) {
62664 pDecoder->outputFormat = internalFormat;
62665 } else {
62666 pDecoder->outputFormat = pConfig->format;
62667 }
62668
62669 if (pConfig->channels == 0) {
62670 pDecoder->outputChannels = internalChannels;
62671 } else {
62672 pDecoder->outputChannels = pConfig->channels;
62673 }
62674
62675 if (pConfig->sampleRate == 0) {
62676 pDecoder->outputSampleRate = internalSampleRate;
62677 } else {
62678 pDecoder->outputSampleRate = pConfig->sampleRate;
62679 }
62680
62681 converterConfig = ma_data_converter_config_init(
62682 internalFormat, pDecoder->outputFormat,
62683 internalChannels, pDecoder->outputChannels,
62684 internalSampleRate, pDecoder->outputSampleRate
62685 );
62686 converterConfig.pChannelMapIn = internalChannelMap;
62687 converterConfig.pChannelMapOut = pConfig->pChannelMap;
62688 converterConfig.channelMixMode = pConfig->channelMixMode;
62689 converterConfig.ditherMode = pConfig->ditherMode;
62690 converterConfig.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */
62691 converterConfig.resampling = pConfig->resampling;
62692
62693 result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter);
62694 if (result != MA_SUCCESS) {
62695 return result;
62696 }
62697
62698 /*
62699 Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll
62700 need this if the data converter does not support calculation of the required input frame count. To
62701 determine support for this we'll just run a test.
62702 */
62703 {
62705
62707 if (result != MA_SUCCESS) {
62708 /*
62709 We were unable to calculate the required input frame count which means we'll need to use
62710 a heap-allocated cache.
62711 */
62712 ma_uint64 inputCacheCapSizeInBytes;
62713
62714 pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels);
62715
62716 /* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */
62717 inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels);
62718 if (inputCacheCapSizeInBytes > MA_SIZE_MAX) {
62720 return MA_OUT_OF_MEMORY;
62721 }
62722
62723 pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks); /* Safe cast to size_t. */
62724 if (pDecoder->pInputCache == NULL) {
62726 return MA_OUT_OF_MEMORY;
62727 }
62728 }
62729 }
62730
62731 return MA_SUCCESS;
62732}
62733
62734
62735
62736static ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
62737{
62738 ma_decoder* pDecoder = (ma_decoder*)pUserData;
62739 MA_ASSERT(pDecoder != NULL);
62740
62741 return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead);
62742}
62743
62744static ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin)
62745{
62746 ma_decoder* pDecoder = (ma_decoder*)pUserData;
62747 MA_ASSERT(pDecoder != NULL);
62748
62749 return ma_decoder_seek_bytes(pDecoder, offset, origin);
62750}
62751
62752static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor)
62753{
62754 ma_decoder* pDecoder = (ma_decoder*)pUserData;
62755 MA_ASSERT(pDecoder != NULL);
62756
62757 return ma_decoder_tell_bytes(pDecoder, pCursor);
62758}
62759
62760
62761static ma_result ma_decoder_init_from_vtable__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62762{
62763 ma_result result;
62764 ma_decoding_backend_config backendConfig;
62765 ma_data_source* pBackend;
62766
62767 MA_ASSERT(pVTable != NULL);
62768 MA_ASSERT(pConfig != NULL);
62769 MA_ASSERT(pDecoder != NULL);
62770
62771 if (pVTable->onInit == NULL) {
62772 return MA_NOT_IMPLEMENTED;
62773 }
62774
62775 backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);
62776
62777 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);
62778 if (result != MA_SUCCESS) {
62779 return result; /* Failed to initialize the backend from this vtable. */
62780 }
62781
62782 /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */
62783 pDecoder->pBackend = pBackend;
62784 pDecoder->pBackendVTable = pVTable;
62785 pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;
62786
62787 return MA_SUCCESS;
62788}
62789
62790static ma_result ma_decoder_init_from_file__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62791{
62792 ma_result result;
62793 ma_decoding_backend_config backendConfig;
62794 ma_data_source* pBackend;
62795
62796 MA_ASSERT(pVTable != NULL);
62797 MA_ASSERT(pConfig != NULL);
62798 MA_ASSERT(pDecoder != NULL);
62799
62800 if (pVTable->onInitFile == NULL) {
62801 return MA_NOT_IMPLEMENTED;
62802 }
62803
62804 backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);
62805
62806 result = pVTable->onInitFile(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend);
62807 if (result != MA_SUCCESS) {
62808 return result; /* Failed to initialize the backend from this vtable. */
62809 }
62810
62811 /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */
62812 pDecoder->pBackend = pBackend;
62813 pDecoder->pBackendVTable = pVTable;
62814 pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;
62815
62816 return MA_SUCCESS;
62817}
62818
62819static ma_result ma_decoder_init_from_file_w__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62820{
62821 ma_result result;
62822 ma_decoding_backend_config backendConfig;
62823 ma_data_source* pBackend;
62824
62825 MA_ASSERT(pVTable != NULL);
62826 MA_ASSERT(pConfig != NULL);
62827 MA_ASSERT(pDecoder != NULL);
62828
62829 if (pVTable->onInitFileW == NULL) {
62830 return MA_NOT_IMPLEMENTED;
62831 }
62832
62833 backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);
62834
62835 result = pVTable->onInitFileW(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend);
62836 if (result != MA_SUCCESS) {
62837 return result; /* Failed to initialize the backend from this vtable. */
62838 }
62839
62840 /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */
62841 pDecoder->pBackend = pBackend;
62842 pDecoder->pBackendVTable = pVTable;
62843 pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;
62844
62845 return MA_SUCCESS;
62846}
62847
62848static ma_result ma_decoder_init_from_memory__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62849{
62850 ma_result result;
62851 ma_decoding_backend_config backendConfig;
62852 ma_data_source* pBackend;
62853
62854 MA_ASSERT(pVTable != NULL);
62855 MA_ASSERT(pConfig != NULL);
62856 MA_ASSERT(pDecoder != NULL);
62857
62858 if (pVTable->onInitMemory == NULL) {
62859 return MA_NOT_IMPLEMENTED;
62860 }
62861
62862 backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);
62863
62864 result = pVTable->onInitMemory(pVTableUserData, pData, dataSize, &backendConfig, &pDecoder->allocationCallbacks, &pBackend);
62865 if (result != MA_SUCCESS) {
62866 return result; /* Failed to initialize the backend from this vtable. */
62867 }
62868
62869 /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */
62870 pDecoder->pBackend = pBackend;
62871 pDecoder->pBackendVTable = pVTable;
62872 pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;
62873
62874 return MA_SUCCESS;
62875}
62876
62877
62878
62879static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62880{
62881 ma_result result = MA_NO_BACKEND;
62882 size_t ivtable;
62883
62884 MA_ASSERT(pConfig != NULL);
62885 MA_ASSERT(pDecoder != NULL);
62886
62887 if (pConfig->ppCustomBackendVTables == NULL) {
62888 return MA_NO_BACKEND;
62889 }
62890
62891 /* The order each backend is listed is what defines the priority. */
62892 for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {
62893 const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];
62894 if (pVTable != NULL) {
62895 result = ma_decoder_init_from_vtable__internal(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder);
62896 if (result == MA_SUCCESS) {
62897 return MA_SUCCESS;
62898 } else {
62899 /* 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. */
62900 result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start);
62901 if (result != MA_SUCCESS) {
62902 return result; /* Failed to seek back to the start. */
62903 }
62904 }
62905 } else {
62906 /* No vtable. */
62907 }
62908 }
62909
62910 /* Getting here means we couldn't find a backend. */
62911 return MA_NO_BACKEND;
62912}
62913
62914static ma_result ma_decoder_init_custom_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62915{
62916 ma_result result = MA_NO_BACKEND;
62917 size_t ivtable;
62918
62919 MA_ASSERT(pConfig != NULL);
62920 MA_ASSERT(pDecoder != NULL);
62921
62922 if (pConfig->ppCustomBackendVTables == NULL) {
62923 return MA_NO_BACKEND;
62924 }
62925
62926 /* The order each backend is listed is what defines the priority. */
62927 for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {
62928 const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];
62929 if (pVTable != NULL) {
62930 result = ma_decoder_init_from_file__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder);
62931 if (result == MA_SUCCESS) {
62932 return MA_SUCCESS;
62933 }
62934 } else {
62935 /* No vtable. */
62936 }
62937 }
62938
62939 /* Getting here means we couldn't find a backend. */
62940 return MA_NO_BACKEND;
62941}
62942
62943static ma_result ma_decoder_init_custom_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62944{
62945 ma_result result = MA_NO_BACKEND;
62946 size_t ivtable;
62947
62948 MA_ASSERT(pConfig != NULL);
62949 MA_ASSERT(pDecoder != NULL);
62950
62951 if (pConfig->ppCustomBackendVTables == NULL) {
62952 return MA_NO_BACKEND;
62953 }
62954
62955 /* The order each backend is listed is what defines the priority. */
62956 for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {
62957 const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];
62958 if (pVTable != NULL) {
62959 result = ma_decoder_init_from_file_w__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder);
62960 if (result == MA_SUCCESS) {
62961 return MA_SUCCESS;
62962 }
62963 } else {
62964 /* No vtable. */
62965 }
62966 }
62967
62968 /* Getting here means we couldn't find a backend. */
62969 return MA_NO_BACKEND;
62970}
62971
62972static ma_result ma_decoder_init_custom_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
62973{
62974 ma_result result = MA_NO_BACKEND;
62975 size_t ivtable;
62976
62977 MA_ASSERT(pConfig != NULL);
62978 MA_ASSERT(pDecoder != NULL);
62979
62980 if (pConfig->ppCustomBackendVTables == NULL) {
62981 return MA_NO_BACKEND;
62982 }
62983
62984 /* The order each backend is listed is what defines the priority. */
62985 for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {
62986 const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];
62987 if (pVTable != NULL) {
62988 result = ma_decoder_init_from_memory__internal(pVTable, pConfig->pCustomBackendUserData, pData, dataSize, pConfig, pDecoder);
62989 if (result == MA_SUCCESS) {
62990 return MA_SUCCESS;
62991 }
62992 } else {
62993 /* No vtable. */
62994 }
62995 }
62996
62997 /* Getting here means we couldn't find a backend. */
62998 return MA_NO_BACKEND;
62999}
63000
63001
63002/* WAV */
63003#ifdef ma_dr_wav_h
63004#define MA_HAS_WAV
63005
63006typedef struct
63007{
63008 ma_data_source_base ds;
63009 ma_read_proc onRead;
63010 ma_seek_proc onSeek;
63011 ma_tell_proc onTell;
63012 void* pReadSeekTellUserData;
63013 ma_format format; /* Can be f32, s16 or s32. */
63014#if !defined(MA_NO_WAV)
63015 ma_dr_wav dr;
63016#endif
63017} ma_wav;
63018
63019MA_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);
63020MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav);
63021MA_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);
63022MA_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);
63023MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks);
63024MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
63025MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex);
63026MA_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);
63027MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor);
63028MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength);
63029
63030
63031static ma_result ma_wav_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
63032{
63033 return ma_wav_read_pcm_frames((ma_wav*)pDataSource, pFramesOut, frameCount, pFramesRead);
63034}
63035
63036static ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
63037{
63038 return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex);
63039}
63040
63041static 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)
63042{
63043 return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
63044}
63045
63046static ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
63047{
63048 return ma_wav_get_cursor_in_pcm_frames((ma_wav*)pDataSource, pCursor);
63049}
63050
63051static ma_result ma_wav_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
63052{
63053 return ma_wav_get_length_in_pcm_frames((ma_wav*)pDataSource, pLength);
63054}
63055
63056static ma_data_source_vtable g_ma_wav_ds_vtable =
63057{
63058 ma_wav_ds_read,
63059 ma_wav_ds_seek,
63060 ma_wav_ds_get_data_format,
63061 ma_wav_ds_get_cursor,
63062 ma_wav_ds_get_length,
63063 NULL, /* onSetLooping */
63064 0
63065};
63066
63067
63068#if !defined(MA_NO_WAV)
63069static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
63070{
63071 ma_wav* pWav = (ma_wav*)pUserData;
63072 ma_result result;
63073 size_t bytesRead;
63074
63075 MA_ASSERT(pWav != NULL);
63076
63077 result = pWav->onRead(pWav->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
63078 (void)result;
63079
63080 return bytesRead;
63081}
63082
63083static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav_seek_origin origin)
63084{
63085 ma_wav* pWav = (ma_wav*)pUserData;
63086 ma_result result;
63087 ma_seek_origin maSeekOrigin;
63088
63089 MA_ASSERT(pWav != NULL);
63090
63091 maSeekOrigin = ma_seek_origin_start;
63092 if (origin == MA_DR_WAV_SEEK_CUR) {
63093 maSeekOrigin = ma_seek_origin_current;
63094 } else if (origin == MA_DR_WAV_SEEK_END) {
63095 maSeekOrigin = ma_seek_origin_end;
63096 }
63097
63098 result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin);
63099 if (result != MA_SUCCESS) {
63100 return MA_FALSE;
63101 }
63102
63103 return MA_TRUE;
63104}
63105
63106static ma_bool32 ma_wav_dr_callback__tell(void* pUserData, ma_int64* pCursor)
63107{
63108 ma_wav* pWav = (ma_wav*)pUserData;
63109 ma_result result;
63110
63111 MA_ASSERT(pWav != NULL);
63112 MA_ASSERT(pCursor != NULL);
63113
63114 if (pWav->onTell == NULL) {
63115 return MA_FALSE; /* Not implemented. */
63116 }
63117
63118 result = pWav->onTell(pWav->pReadSeekTellUserData, pCursor);
63119 if (result != MA_SUCCESS) {
63120 return MA_FALSE; /* Failed to tell. */
63121 }
63122
63123 return MA_TRUE;
63124}
63125#endif
63126
63127static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav)
63128{
63129 ma_result result;
63130 ma_data_source_config dataSourceConfig;
63131
63132 if (pWav == NULL) {
63133 return MA_INVALID_ARGS;
63134 }
63135
63136 MA_ZERO_OBJECT(pWav);
63137 pWav->format = ma_format_unknown; /* Use closest match to source file by default. */
63138
63139 if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) {
63140 pWav->format = pConfig->preferredFormat;
63141 } else {
63142 /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
63143 }
63144
63145 dataSourceConfig = ma_data_source_config_init();
63146 dataSourceConfig.vtable = &g_ma_wav_ds_vtable;
63147
63148 result = ma_data_source_init(&dataSourceConfig, &pWav->ds);
63149 if (result != MA_SUCCESS) {
63150 return result; /* Failed to initialize the base data source. */
63151 }
63152
63153 return MA_SUCCESS;
63154}
63155
63156static ma_result ma_wav_post_init(ma_wav* pWav)
63157{
63158 /*
63159 If an explicit format was not specified, try picking the closest match based on the internal
63160 format. The format needs to be supported by miniaudio.
63161 */
63162 if (pWav->format == ma_format_unknown) {
63163 switch (pWav->dr.translatedFormatTag)
63164 {
63165 case MA_DR_WAVE_FORMAT_PCM:
63166 {
63167 if (pWav->dr.bitsPerSample == 8) {
63168 pWav->format = ma_format_u8;
63169 } else if (pWav->dr.bitsPerSample == 16) {
63170 pWav->format = ma_format_s16;
63171 } else if (pWav->dr.bitsPerSample == 24) {
63172 pWav->format = ma_format_s24;
63173 } else if (pWav->dr.bitsPerSample == 32) {
63174 pWav->format = ma_format_s32;
63175 }
63176 } break;
63177
63178 case MA_DR_WAVE_FORMAT_IEEE_FLOAT:
63179 {
63180 if (pWav->dr.bitsPerSample == 32) {
63181 pWav->format = ma_format_f32;
63182 }
63183 } break;
63184
63185 default: break;
63186 }
63187
63188 /* Fall back to f32 if we couldn't find anything. */
63189 if (pWav->format == ma_format_unknown) {
63190 pWav->format = ma_format_f32;
63191 }
63192 }
63193
63194 return MA_SUCCESS;
63195}
63196
63197MA_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)
63198{
63199 ma_result result;
63200
63201 result = ma_wav_init_internal(pConfig, pWav);
63202 if (result != MA_SUCCESS) {
63203 return result;
63204 }
63205
63206 if (onRead == NULL || onSeek == NULL) {
63207 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
63208 }
63209
63210 pWav->onRead = onRead;
63211 pWav->onSeek = onSeek;
63212 pWav->onTell = onTell;
63213 pWav->pReadSeekTellUserData = pReadSeekTellUserData;
63214
63215 #if !defined(MA_NO_WAV)
63216 {
63217 ma_bool32 wavResult;
63218
63219 wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, ma_wav_dr_callback__tell, pWav, pAllocationCallbacks);
63220 if (wavResult != MA_TRUE) {
63221 return MA_INVALID_FILE;
63222 }
63223
63224 ma_wav_post_init(pWav);
63225
63226 return MA_SUCCESS;
63227 }
63228 #else
63229 {
63230 /* wav is disabled. */
63231 (void)pAllocationCallbacks;
63232 return MA_NOT_IMPLEMENTED;
63233 }
63234 #endif
63235}
63236
63237MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav)
63238{
63239 ma_result result;
63240
63241 result = ma_wav_init_internal(pConfig, pWav);
63242 if (result != MA_SUCCESS) {
63243 return result;
63244 }
63245
63246 #if !defined(MA_NO_WAV)
63247 {
63248 ma_bool32 wavResult;
63249
63250 wavResult = ma_dr_wav_init_file(&pWav->dr, pFilePath, pAllocationCallbacks);
63251 if (wavResult != MA_TRUE) {
63252 return MA_INVALID_FILE;
63253 }
63254
63255 ma_wav_post_init(pWav);
63256
63257 return MA_SUCCESS;
63258 }
63259 #else
63260 {
63261 /* wav is disabled. */
63262 (void)pFilePath;
63263 (void)pAllocationCallbacks;
63264 return MA_NOT_IMPLEMENTED;
63265 }
63266 #endif
63267}
63268
63269MA_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)
63270{
63271 ma_result result;
63272
63273 result = ma_wav_init_internal(pConfig, pWav);
63274 if (result != MA_SUCCESS) {
63275 return result;
63276 }
63277
63278 #if !defined(MA_NO_WAV)
63279 {
63280 ma_bool32 wavResult;
63281
63282 wavResult = ma_dr_wav_init_file_w(&pWav->dr, pFilePath, pAllocationCallbacks);
63283 if (wavResult != MA_TRUE) {
63284 return MA_INVALID_FILE;
63285 }
63286
63287 ma_wav_post_init(pWav);
63288
63289 return MA_SUCCESS;
63290 }
63291 #else
63292 {
63293 /* wav is disabled. */
63294 (void)pFilePath;
63295 (void)pAllocationCallbacks;
63296 return MA_NOT_IMPLEMENTED;
63297 }
63298 #endif
63299}
63300
63301MA_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)
63302{
63303 ma_result result;
63304
63305 result = ma_wav_init_internal(pConfig, pWav);
63306 if (result != MA_SUCCESS) {
63307 return result;
63308 }
63309
63310 #if !defined(MA_NO_WAV)
63311 {
63312 ma_bool32 wavResult;
63313
63314 wavResult = ma_dr_wav_init_memory(&pWav->dr, pData, dataSize, pAllocationCallbacks);
63315 if (wavResult != MA_TRUE) {
63316 return MA_INVALID_FILE;
63317 }
63318
63319 ma_wav_post_init(pWav);
63320
63321 return MA_SUCCESS;
63322 }
63323 #else
63324 {
63325 /* wav is disabled. */
63326 (void)pData;
63327 (void)dataSize;
63328 (void)pAllocationCallbacks;
63329 return MA_NOT_IMPLEMENTED;
63330 }
63331 #endif
63332}
63333
63334MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks)
63335{
63336 if (pWav == NULL) {
63337 return;
63338 }
63339
63340 (void)pAllocationCallbacks;
63341
63342 #if !defined(MA_NO_WAV)
63343 {
63344 ma_dr_wav_uninit(&pWav->dr);
63345 }
63346 #else
63347 {
63348 /* wav is disabled. Should never hit this since initialization would have failed. */
63349 MA_ASSERT(MA_FALSE);
63350 }
63351 #endif
63352
63353 ma_data_source_uninit(&pWav->ds);
63354}
63355
63356MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
63357{
63358 if (pFramesRead != NULL) {
63359 *pFramesRead = 0;
63360 }
63361
63362 if (frameCount == 0) {
63363 return MA_INVALID_ARGS;
63364 }
63365
63366 if (pWav == NULL) {
63367 return MA_INVALID_ARGS;
63368 }
63369
63370 #if !defined(MA_NO_WAV)
63371 {
63372 /* We always use floating point format. */
63373 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
63374 ma_uint64 totalFramesRead = 0;
63375 ma_format format;
63376
63377 ma_wav_get_data_format(pWav, &format, NULL, NULL, NULL, 0);
63378
63379 switch (format)
63380 {
63381 case ma_format_f32:
63382 {
63383 totalFramesRead = ma_dr_wav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut);
63384 } break;
63385
63386 case ma_format_s16:
63387 {
63388 totalFramesRead = ma_dr_wav_read_pcm_frames_s16(&pWav->dr, frameCount, (ma_int16*)pFramesOut);
63389 } break;
63390
63391 case ma_format_s32:
63392 {
63393 totalFramesRead = ma_dr_wav_read_pcm_frames_s32(&pWav->dr, frameCount, (ma_int32*)pFramesOut);
63394 } break;
63395
63396 /* Fallback to a raw read. */
63397 case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */
63398 default:
63399 {
63400 totalFramesRead = ma_dr_wav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut);
63401 } break;
63402 }
63403
63404 /* In the future we'll update ma_dr_wav to return MA_AT_END for us. */
63405 if (totalFramesRead == 0) {
63406 result = MA_AT_END;
63407 }
63408
63409 if (pFramesRead != NULL) {
63410 *pFramesRead = totalFramesRead;
63411 }
63412
63413 if (result == MA_SUCCESS && totalFramesRead == 0) {
63414 result = MA_AT_END;
63415 }
63416
63417 return result;
63418 }
63419 #else
63420 {
63421 /* wav is disabled. Should never hit this since initialization would have failed. */
63422 MA_ASSERT(MA_FALSE);
63423
63424 (void)pFramesOut;
63425 (void)frameCount;
63426 (void)pFramesRead;
63427
63428 return MA_NOT_IMPLEMENTED;
63429 }
63430 #endif
63431}
63432
63433MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex)
63434{
63435 if (pWav == NULL) {
63436 return MA_INVALID_ARGS;
63437 }
63438
63439 #if !defined(MA_NO_WAV)
63440 {
63441 ma_bool32 wavResult;
63442
63443 wavResult = ma_dr_wav_seek_to_pcm_frame(&pWav->dr, frameIndex);
63444 if (wavResult != MA_TRUE) {
63445 return MA_ERROR;
63446 }
63447
63448 return MA_SUCCESS;
63449 }
63450 #else
63451 {
63452 /* wav is disabled. Should never hit this since initialization would have failed. */
63453 MA_ASSERT(MA_FALSE);
63454
63455 (void)frameIndex;
63456
63457 return MA_NOT_IMPLEMENTED;
63458 }
63459 #endif
63460}
63461
63462MA_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)
63463{
63464 /* Defaults for safety. */
63465 if (pFormat != NULL) {
63466 *pFormat = ma_format_unknown;
63467 }
63468 if (pChannels != NULL) {
63469 *pChannels = 0;
63470 }
63471 if (pSampleRate != NULL) {
63472 *pSampleRate = 0;
63473 }
63474 if (pChannelMap != NULL) {
63475 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
63476 }
63477
63478 if (pWav == NULL) {
63479 return MA_INVALID_OPERATION;
63480 }
63481
63482 if (pFormat != NULL) {
63483 *pFormat = pWav->format;
63484 }
63485
63486 #if !defined(MA_NO_WAV)
63487 {
63488 if (pChannels != NULL) {
63489 *pChannels = pWav->dr.channels;
63490 }
63491
63492 if (pSampleRate != NULL) {
63493 *pSampleRate = pWav->dr.sampleRate;
63494 }
63495
63496 if (pChannelMap != NULL) {
63497 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pWav->dr.channels);
63498 }
63499
63500 return MA_SUCCESS;
63501 }
63502 #else
63503 {
63504 /* wav is disabled. Should never hit this since initialization would have failed. */
63505 MA_ASSERT(MA_FALSE);
63506 return MA_NOT_IMPLEMENTED;
63507 }
63508 #endif
63509}
63510
63511MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor)
63512{
63513 if (pCursor == NULL) {
63514 return MA_INVALID_ARGS;
63515 }
63516
63517 *pCursor = 0; /* Safety. */
63518
63519 if (pWav == NULL) {
63520 return MA_INVALID_ARGS;
63521 }
63522
63523 #if !defined(MA_NO_WAV)
63524 {
63525 ma_result wavResult = ma_dr_wav_get_cursor_in_pcm_frames(&pWav->dr, pCursor);
63526 if (wavResult != MA_SUCCESS) {
63527 return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */
63528 }
63529
63530 return MA_SUCCESS;
63531 }
63532 #else
63533 {
63534 /* wav is disabled. Should never hit this since initialization would have failed. */
63535 MA_ASSERT(MA_FALSE);
63536 return MA_NOT_IMPLEMENTED;
63537 }
63538 #endif
63539}
63540
63541MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength)
63542{
63543 if (pLength == NULL) {
63544 return MA_INVALID_ARGS;
63545 }
63546
63547 *pLength = 0; /* Safety. */
63548
63549 if (pWav == NULL) {
63550 return MA_INVALID_ARGS;
63551 }
63552
63553 #if !defined(MA_NO_WAV)
63554 {
63555 ma_result wavResult = ma_dr_wav_get_length_in_pcm_frames(&pWav->dr, pLength);
63556 if (wavResult != MA_SUCCESS) {
63557 return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */
63558 }
63559
63560 return MA_SUCCESS;
63561 }
63562 #else
63563 {
63564 /* wav is disabled. Should never hit this since initialization would have failed. */
63565 MA_ASSERT(MA_FALSE);
63566 return MA_NOT_IMPLEMENTED;
63567 }
63568 #endif
63569}
63570
63571
63572static 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)
63573{
63574 ma_result result;
63575 ma_wav* pWav;
63576
63577 (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. */
63578
63579 /* For now we're just allocating the decoder backend on the heap. */
63580 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
63581 if (pWav == NULL) {
63582 return MA_OUT_OF_MEMORY;
63583 }
63584
63585 result = ma_wav_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pWav);
63586 if (result != MA_SUCCESS) {
63587 ma_free(pWav, pAllocationCallbacks);
63588 return result;
63589 }
63590
63591 *ppBackend = pWav;
63592
63593 return MA_SUCCESS;
63594}
63595
63596static 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)
63597{
63598 ma_result result;
63599 ma_wav* pWav;
63600
63601 (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. */
63602
63603 /* For now we're just allocating the decoder backend on the heap. */
63604 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
63605 if (pWav == NULL) {
63606 return MA_OUT_OF_MEMORY;
63607 }
63608
63609 result = ma_wav_init_file(pFilePath, pConfig, pAllocationCallbacks, pWav);
63610 if (result != MA_SUCCESS) {
63611 ma_free(pWav, pAllocationCallbacks);
63612 return result;
63613 }
63614
63615 *ppBackend = pWav;
63616
63617 return MA_SUCCESS;
63618}
63619
63620static 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)
63621{
63622 ma_result result;
63623 ma_wav* pWav;
63624
63625 (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. */
63626
63627 /* For now we're just allocating the decoder backend on the heap. */
63628 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
63629 if (pWav == NULL) {
63630 return MA_OUT_OF_MEMORY;
63631 }
63632
63633 result = ma_wav_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pWav);
63634 if (result != MA_SUCCESS) {
63635 ma_free(pWav, pAllocationCallbacks);
63636 return result;
63637 }
63638
63639 *ppBackend = pWav;
63640
63641 return MA_SUCCESS;
63642}
63643
63644static 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)
63645{
63646 ma_result result;
63647 ma_wav* pWav;
63648
63649 (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. */
63650
63651 /* For now we're just allocating the decoder backend on the heap. */
63652 pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
63653 if (pWav == NULL) {
63654 return MA_OUT_OF_MEMORY;
63655 }
63656
63657 result = ma_wav_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pWav);
63658 if (result != MA_SUCCESS) {
63659 ma_free(pWav, pAllocationCallbacks);
63660 return result;
63661 }
63662
63663 *ppBackend = pWav;
63664
63665 return MA_SUCCESS;
63666}
63667
63668static void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
63669{
63670 ma_wav* pWav = (ma_wav*)pBackend;
63671
63672 (void)pUserData;
63673
63674 ma_wav_uninit(pWav, pAllocationCallbacks);
63675 ma_free(pWav, pAllocationCallbacks);
63676}
63677
63678static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav =
63679{
63680 ma_decoding_backend_init__wav,
63681 ma_decoding_backend_init_file__wav,
63682 ma_decoding_backend_init_file_w__wav,
63683 ma_decoding_backend_init_memory__wav,
63684 ma_decoding_backend_uninit__wav
63685};
63686
63687static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
63688{
63689 return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder);
63690}
63691
63692static ma_result ma_decoder_init_wav_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
63693{
63694 return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder);
63695}
63696
63697static ma_result ma_decoder_init_wav_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
63698{
63699 return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder);
63700}
63701
63702static ma_result ma_decoder_init_wav_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
63703{
63704 return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_wav, NULL, pData, dataSize, pConfig, pDecoder);
63705}
63706#endif /* ma_dr_wav_h */
63707
63708/* FLAC */
63709#ifdef ma_dr_flac_h
63710#define MA_HAS_FLAC
63711
63712typedef struct
63713{
63714 ma_data_source_base ds;
63715 ma_read_proc onRead;
63716 ma_seek_proc onSeek;
63717 ma_tell_proc onTell;
63718 void* pReadSeekTellUserData;
63719 ma_format format; /* Can be f32, s16 or s32. */
63720#if !defined(MA_NO_FLAC)
63721 ma_dr_flac* dr;
63722#endif
63723} ma_flac;
63724
63725MA_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);
63726MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac);
63727MA_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);
63728MA_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);
63729MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks);
63730MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
63731MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex);
63732MA_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);
63733MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor);
63734MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength);
63735
63736
63737static ma_result ma_flac_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
63738{
63739 return ma_flac_read_pcm_frames((ma_flac*)pDataSource, pFramesOut, frameCount, pFramesRead);
63740}
63741
63742static ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
63743{
63744 return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex);
63745}
63746
63747static 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)
63748{
63749 return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
63750}
63751
63752static ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
63753{
63754 return ma_flac_get_cursor_in_pcm_frames((ma_flac*)pDataSource, pCursor);
63755}
63756
63757static ma_result ma_flac_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
63758{
63759 return ma_flac_get_length_in_pcm_frames((ma_flac*)pDataSource, pLength);
63760}
63761
63762static ma_data_source_vtable g_ma_flac_ds_vtable =
63763{
63764 ma_flac_ds_read,
63765 ma_flac_ds_seek,
63766 ma_flac_ds_get_data_format,
63767 ma_flac_ds_get_cursor,
63768 ma_flac_ds_get_length,
63769 NULL, /* onSetLooping */
63770 0
63771};
63772
63773
63774#if !defined(MA_NO_FLAC)
63775static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
63776{
63777 ma_flac* pFlac = (ma_flac*)pUserData;
63778 ma_result result;
63779 size_t bytesRead;
63780
63781 MA_ASSERT(pFlac != NULL);
63782
63783 result = pFlac->onRead(pFlac->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
63784 (void)result;
63785
63786 return bytesRead;
63787}
63788
63789static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_flac_seek_origin origin)
63790{
63791 ma_flac* pFlac = (ma_flac*)pUserData;
63792 ma_result result;
63793 ma_seek_origin maSeekOrigin;
63794
63795 MA_ASSERT(pFlac != NULL);
63796
63797 maSeekOrigin = ma_seek_origin_start;
63798 if (origin == MA_DR_FLAC_SEEK_CUR) {
63799 maSeekOrigin = ma_seek_origin_current;
63800 } else if (origin == MA_DR_FLAC_SEEK_END) {
63801 maSeekOrigin = ma_seek_origin_end;
63802 }
63803
63804 result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin);
63805 if (result != MA_SUCCESS) {
63806 return MA_FALSE;
63807 }
63808
63809 return MA_TRUE;
63810}
63811
63812static ma_bool32 ma_flac_dr_callback__tell(void* pUserData, ma_int64* pCursor)
63813{
63814 ma_flac* pFlac = (ma_flac*)pUserData;
63815 ma_result result;
63816
63817 MA_ASSERT(pFlac != NULL);
63818 MA_ASSERT(pCursor != NULL);
63819
63820 if (pFlac->onTell == NULL) {
63821 return MA_FALSE; /* Not implemented. */
63822 }
63823
63824 result = pFlac->onTell(pFlac->pReadSeekTellUserData, pCursor);
63825 if (result != MA_SUCCESS) {
63826 return MA_FALSE; /* Failed to tell. */
63827 }
63828
63829 return MA_TRUE;
63830}
63831#endif
63832
63833static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac)
63834{
63835 ma_result result;
63836 ma_data_source_config dataSourceConfig;
63837
63838 if (pFlac == NULL) {
63839 return MA_INVALID_ARGS;
63840 }
63841
63842 MA_ZERO_OBJECT(pFlac);
63843 pFlac->format = ma_format_f32; /* f32 by default. */
63844
63845 if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) {
63846 pFlac->format = pConfig->preferredFormat;
63847 } else {
63848 /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
63849 }
63850
63851 dataSourceConfig = ma_data_source_config_init();
63852 dataSourceConfig.vtable = &g_ma_flac_ds_vtable;
63853
63854 result = ma_data_source_init(&dataSourceConfig, &pFlac->ds);
63855 if (result != MA_SUCCESS) {
63856 return result; /* Failed to initialize the base data source. */
63857 }
63858
63859 return MA_SUCCESS;
63860}
63861
63862MA_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)
63863{
63864 ma_result result;
63865
63866 result = ma_flac_init_internal(pConfig, pFlac);
63867 if (result != MA_SUCCESS) {
63868 return result;
63869 }
63870
63871 if (onRead == NULL || onSeek == NULL) {
63872 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
63873 }
63874
63875 pFlac->onRead = onRead;
63876 pFlac->onSeek = onSeek;
63877 pFlac->onTell = onTell;
63878 pFlac->pReadSeekTellUserData = pReadSeekTellUserData;
63879
63880 #if !defined(MA_NO_FLAC)
63881 {
63882 pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, ma_flac_dr_callback__tell, pFlac, pAllocationCallbacks);
63883 if (pFlac->dr == NULL) {
63884 return MA_INVALID_FILE;
63885 }
63886
63887 return MA_SUCCESS;
63888 }
63889 #else
63890 {
63891 /* flac is disabled. */
63892 (void)pAllocationCallbacks;
63893 return MA_NOT_IMPLEMENTED;
63894 }
63895 #endif
63896}
63897
63898MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac)
63899{
63900 ma_result result;
63901
63902 result = ma_flac_init_internal(pConfig, pFlac);
63903 if (result != MA_SUCCESS) {
63904 return result;
63905 }
63906
63907 #if !defined(MA_NO_FLAC)
63908 {
63909 pFlac->dr = ma_dr_flac_open_file(pFilePath, pAllocationCallbacks);
63910 if (pFlac->dr == NULL) {
63911 return MA_INVALID_FILE;
63912 }
63913
63914 return MA_SUCCESS;
63915 }
63916 #else
63917 {
63918 /* flac is disabled. */
63919 (void)pFilePath;
63920 (void)pAllocationCallbacks;
63921 return MA_NOT_IMPLEMENTED;
63922 }
63923 #endif
63924}
63925
63926MA_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)
63927{
63928 ma_result result;
63929
63930 result = ma_flac_init_internal(pConfig, pFlac);
63931 if (result != MA_SUCCESS) {
63932 return result;
63933 }
63934
63935 #if !defined(MA_NO_FLAC)
63936 {
63937 pFlac->dr = ma_dr_flac_open_file_w(pFilePath, pAllocationCallbacks);
63938 if (pFlac->dr == NULL) {
63939 return MA_INVALID_FILE;
63940 }
63941
63942 return MA_SUCCESS;
63943 }
63944 #else
63945 {
63946 /* flac is disabled. */
63947 (void)pFilePath;
63948 (void)pAllocationCallbacks;
63949 return MA_NOT_IMPLEMENTED;
63950 }
63951 #endif
63952}
63953
63954MA_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)
63955{
63956 ma_result result;
63957
63958 result = ma_flac_init_internal(pConfig, pFlac);
63959 if (result != MA_SUCCESS) {
63960 return result;
63961 }
63962
63963 #if !defined(MA_NO_FLAC)
63964 {
63965 pFlac->dr = ma_dr_flac_open_memory(pData, dataSize, pAllocationCallbacks);
63966 if (pFlac->dr == NULL) {
63967 return MA_INVALID_FILE;
63968 }
63969
63970 return MA_SUCCESS;
63971 }
63972 #else
63973 {
63974 /* flac is disabled. */
63975 (void)pData;
63976 (void)dataSize;
63977 (void)pAllocationCallbacks;
63978 return MA_NOT_IMPLEMENTED;
63979 }
63980 #endif
63981}
63982
63983MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks)
63984{
63985 if (pFlac == NULL) {
63986 return;
63987 }
63988
63989 (void)pAllocationCallbacks;
63990
63991 #if !defined(MA_NO_FLAC)
63992 {
63993 ma_dr_flac_close(pFlac->dr);
63994 }
63995 #else
63996 {
63997 /* flac is disabled. Should never hit this since initialization would have failed. */
63998 MA_ASSERT(MA_FALSE);
63999 }
64000 #endif
64001
64002 ma_data_source_uninit(&pFlac->ds);
64003}
64004
64005MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
64006{
64007 if (pFramesRead != NULL) {
64008 *pFramesRead = 0;
64009 }
64010
64011 if (frameCount == 0) {
64012 return MA_INVALID_ARGS;
64013 }
64014
64015 if (pFlac == NULL) {
64016 return MA_INVALID_ARGS;
64017 }
64018
64019 #if !defined(MA_NO_FLAC)
64020 {
64021 /* We always use floating point format. */
64022 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
64023 ma_uint64 totalFramesRead = 0;
64024 ma_format format;
64025
64026 ma_flac_get_data_format(pFlac, &format, NULL, NULL, NULL, 0);
64027
64028 switch (format)
64029 {
64030 case ma_format_f32:
64031 {
64032 totalFramesRead = ma_dr_flac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut);
64033 } break;
64034
64035 case ma_format_s16:
64036 {
64037 totalFramesRead = ma_dr_flac_read_pcm_frames_s16(pFlac->dr, frameCount, (ma_int16*)pFramesOut);
64038 } break;
64039
64040 case ma_format_s32:
64041 {
64042 totalFramesRead = ma_dr_flac_read_pcm_frames_s32(pFlac->dr, frameCount, (ma_int32*)pFramesOut);
64043 } break;
64044
64045 case ma_format_u8:
64046 case ma_format_s24:
64047 case ma_format_unknown:
64048 default:
64049 {
64050 return MA_INVALID_OPERATION;
64051 };
64052 }
64053
64054 /* In the future we'll update ma_dr_flac to return MA_AT_END for us. */
64055 if (totalFramesRead == 0) {
64056 result = MA_AT_END;
64057 }
64058
64059 if (pFramesRead != NULL) {
64060 *pFramesRead = totalFramesRead;
64061 }
64062
64063 if (result == MA_SUCCESS && totalFramesRead == 0) {
64064 result = MA_AT_END;
64065 }
64066
64067 return result;
64068 }
64069 #else
64070 {
64071 /* flac is disabled. Should never hit this since initialization would have failed. */
64072 MA_ASSERT(MA_FALSE);
64073
64074 (void)pFramesOut;
64075 (void)frameCount;
64076 (void)pFramesRead;
64077
64078 return MA_NOT_IMPLEMENTED;
64079 }
64080 #endif
64081}
64082
64083MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex)
64084{
64085 if (pFlac == NULL) {
64086 return MA_INVALID_ARGS;
64087 }
64088
64089 #if !defined(MA_NO_FLAC)
64090 {
64091 ma_bool32 flacResult;
64092
64093 flacResult = ma_dr_flac_seek_to_pcm_frame(pFlac->dr, frameIndex);
64094 if (flacResult != MA_TRUE) {
64095 return MA_ERROR;
64096 }
64097
64098 return MA_SUCCESS;
64099 }
64100 #else
64101 {
64102 /* flac is disabled. Should never hit this since initialization would have failed. */
64103 MA_ASSERT(MA_FALSE);
64104
64105 (void)frameIndex;
64106
64107 return MA_NOT_IMPLEMENTED;
64108 }
64109 #endif
64110}
64111
64112MA_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)
64113{
64114 /* Defaults for safety. */
64115 if (pFormat != NULL) {
64116 *pFormat = ma_format_unknown;
64117 }
64118 if (pChannels != NULL) {
64119 *pChannels = 0;
64120 }
64121 if (pSampleRate != NULL) {
64122 *pSampleRate = 0;
64123 }
64124 if (pChannelMap != NULL) {
64125 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
64126 }
64127
64128 if (pFlac == NULL) {
64129 return MA_INVALID_OPERATION;
64130 }
64131
64132 if (pFormat != NULL) {
64133 *pFormat = pFlac->format;
64134 }
64135
64136 #if !defined(MA_NO_FLAC)
64137 {
64138 if (pChannels != NULL) {
64139 *pChannels = pFlac->dr->channels;
64140 }
64141
64142 if (pSampleRate != NULL) {
64143 *pSampleRate = pFlac->dr->sampleRate;
64144 }
64145
64146 if (pChannelMap != NULL) {
64147 ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pFlac->dr->channels);
64148 }
64149
64150 return MA_SUCCESS;
64151 }
64152 #else
64153 {
64154 /* flac is disabled. Should never hit this since initialization would have failed. */
64155 MA_ASSERT(MA_FALSE);
64156 return MA_NOT_IMPLEMENTED;
64157 }
64158 #endif
64159}
64160
64161MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor)
64162{
64163 if (pCursor == NULL) {
64164 return MA_INVALID_ARGS;
64165 }
64166
64167 *pCursor = 0; /* Safety. */
64168
64169 if (pFlac == NULL) {
64170 return MA_INVALID_ARGS;
64171 }
64172
64173 #if !defined(MA_NO_FLAC)
64174 {
64175 *pCursor = pFlac->dr->currentPCMFrame;
64176
64177 return MA_SUCCESS;
64178 }
64179 #else
64180 {
64181 /* flac is disabled. Should never hit this since initialization would have failed. */
64182 MA_ASSERT(MA_FALSE);
64183 return MA_NOT_IMPLEMENTED;
64184 }
64185 #endif
64186}
64187
64188MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength)
64189{
64190 if (pLength == NULL) {
64191 return MA_INVALID_ARGS;
64192 }
64193
64194 *pLength = 0; /* Safety. */
64195
64196 if (pFlac == NULL) {
64197 return MA_INVALID_ARGS;
64198 }
64199
64200 #if !defined(MA_NO_FLAC)
64201 {
64202 *pLength = pFlac->dr->totalPCMFrameCount;
64203
64204 return MA_SUCCESS;
64205 }
64206 #else
64207 {
64208 /* flac is disabled. Should never hit this since initialization would have failed. */
64209 MA_ASSERT(MA_FALSE);
64210 return MA_NOT_IMPLEMENTED;
64211 }
64212 #endif
64213}
64214
64215
64216static 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)
64217{
64218 ma_result result;
64219 ma_flac* pFlac;
64220
64221 (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. */
64222
64223 /* For now we're just allocating the decoder backend on the heap. */
64224 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
64225 if (pFlac == NULL) {
64226 return MA_OUT_OF_MEMORY;
64227 }
64228
64229 result = ma_flac_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pFlac);
64230 if (result != MA_SUCCESS) {
64231 ma_free(pFlac, pAllocationCallbacks);
64232 return result;
64233 }
64234
64235 *ppBackend = pFlac;
64236
64237 return MA_SUCCESS;
64238}
64239
64240static 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)
64241{
64242 ma_result result;
64243 ma_flac* pFlac;
64244
64245 (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. */
64246
64247 /* For now we're just allocating the decoder backend on the heap. */
64248 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
64249 if (pFlac == NULL) {
64250 return MA_OUT_OF_MEMORY;
64251 }
64252
64253 result = ma_flac_init_file(pFilePath, pConfig, pAllocationCallbacks, pFlac);
64254 if (result != MA_SUCCESS) {
64255 ma_free(pFlac, pAllocationCallbacks);
64256 return result;
64257 }
64258
64259 *ppBackend = pFlac;
64260
64261 return MA_SUCCESS;
64262}
64263
64264static 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)
64265{
64266 ma_result result;
64267 ma_flac* pFlac;
64268
64269 (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. */
64270
64271 /* For now we're just allocating the decoder backend on the heap. */
64272 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
64273 if (pFlac == NULL) {
64274 return MA_OUT_OF_MEMORY;
64275 }
64276
64277 result = ma_flac_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pFlac);
64278 if (result != MA_SUCCESS) {
64279 ma_free(pFlac, pAllocationCallbacks);
64280 return result;
64281 }
64282
64283 *ppBackend = pFlac;
64284
64285 return MA_SUCCESS;
64286}
64287
64288static 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)
64289{
64290 ma_result result;
64291 ma_flac* pFlac;
64292
64293 (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. */
64294
64295 /* For now we're just allocating the decoder backend on the heap. */
64296 pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
64297 if (pFlac == NULL) {
64298 return MA_OUT_OF_MEMORY;
64299 }
64300
64301 result = ma_flac_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pFlac);
64302 if (result != MA_SUCCESS) {
64303 ma_free(pFlac, pAllocationCallbacks);
64304 return result;
64305 }
64306
64307 *ppBackend = pFlac;
64308
64309 return MA_SUCCESS;
64310}
64311
64312static void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
64313{
64314 ma_flac* pFlac = (ma_flac*)pBackend;
64315
64316 (void)pUserData;
64317
64318 ma_flac_uninit(pFlac, pAllocationCallbacks);
64319 ma_free(pFlac, pAllocationCallbacks);
64320}
64321
64322static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac =
64323{
64324 ma_decoding_backend_init__flac,
64325 ma_decoding_backend_init_file__flac,
64326 ma_decoding_backend_init_file_w__flac,
64327 ma_decoding_backend_init_memory__flac,
64328 ma_decoding_backend_uninit__flac
64329};
64330
64331static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64332{
64333 return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder);
64334}
64335
64336static ma_result ma_decoder_init_flac_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64337{
64338 return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder);
64339}
64340
64341static ma_result ma_decoder_init_flac_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64342{
64343 return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder);
64344}
64345
64346static ma_result ma_decoder_init_flac_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
64347{
64348 return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_flac, NULL, pData, dataSize, pConfig, pDecoder);
64349}
64350#endif /* ma_dr_flac_h */
64351
64352/* MP3 */
64353#ifdef ma_dr_mp3_h
64354#define MA_HAS_MP3
64355
64356typedef struct
64357{
64358 ma_data_source_base ds;
64359 ma_read_proc onRead;
64360 ma_seek_proc onSeek;
64361 ma_tell_proc onTell;
64362 void* pReadSeekTellUserData;
64363 ma_format format; /* Can be f32 or s16. */
64364#if !defined(MA_NO_MP3)
64365 ma_dr_mp3 dr;
64366 ma_uint32 seekPointCount;
64367 ma_dr_mp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */
64368#endif
64369} ma_mp3;
64370
64371MA_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);
64372MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3);
64373MA_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);
64374MA_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);
64375MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks);
64376MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
64377MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex);
64378MA_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);
64379MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor);
64380MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength);
64381
64382
64383static ma_result ma_mp3_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
64384{
64385 return ma_mp3_read_pcm_frames((ma_mp3*)pDataSource, pFramesOut, frameCount, pFramesRead);
64386}
64387
64388static ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
64389{
64390 return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex);
64391}
64392
64393static 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)
64394{
64395 return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
64396}
64397
64398static ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
64399{
64400 return ma_mp3_get_cursor_in_pcm_frames((ma_mp3*)pDataSource, pCursor);
64401}
64402
64403static ma_result ma_mp3_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
64404{
64405 return ma_mp3_get_length_in_pcm_frames((ma_mp3*)pDataSource, pLength);
64406}
64407
64408static ma_data_source_vtable g_ma_mp3_ds_vtable =
64409{
64410 ma_mp3_ds_read,
64411 ma_mp3_ds_seek,
64412 ma_mp3_ds_get_data_format,
64413 ma_mp3_ds_get_cursor,
64414 ma_mp3_ds_get_length,
64415 NULL, /* onSetLooping */
64416 0
64417};
64418
64419
64420#if !defined(MA_NO_MP3)
64421static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
64422{
64423 ma_mp3* pMP3 = (ma_mp3*)pUserData;
64424 ma_result result;
64425 size_t bytesRead;
64426
64427 MA_ASSERT(pMP3 != NULL);
64428
64429 result = pMP3->onRead(pMP3->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
64430 (void)result;
64431
64432 return bytesRead;
64433}
64434
64435static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3_seek_origin origin)
64436{
64437 ma_mp3* pMP3 = (ma_mp3*)pUserData;
64438 ma_result result;
64439 ma_seek_origin maSeekOrigin;
64440
64441 MA_ASSERT(pMP3 != NULL);
64442
64443 if (origin == MA_DR_MP3_SEEK_SET) {
64444 maSeekOrigin = ma_seek_origin_start;
64445 } else if (origin == MA_DR_MP3_SEEK_END) {
64446 maSeekOrigin = ma_seek_origin_end;
64447 } else {
64448 maSeekOrigin = ma_seek_origin_current;
64449 }
64450
64451 result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin);
64452 if (result != MA_SUCCESS) {
64453 return MA_FALSE;
64454 }
64455
64456 return MA_TRUE;
64457}
64458
64459static ma_bool32 ma_mp3_dr_callback__tell(void* pUserData, ma_int64* pCursor)
64460{
64461 ma_mp3* pMP3 = (ma_mp3*)pUserData;
64462 ma_result result;
64463
64464 MA_ASSERT(pMP3 != NULL);
64465
64466 result = pMP3->onTell(pMP3->pReadSeekTellUserData, pCursor);
64467 if (result != MA_SUCCESS) {
64468 return MA_FALSE;
64469 }
64470
64471 return MA_TRUE;
64472}
64473#endif
64474
64475static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3)
64476{
64477 ma_result result;
64478 ma_data_source_config dataSourceConfig;
64479
64480 if (pMP3 == NULL) {
64481 return MA_INVALID_ARGS;
64482 }
64483
64484 MA_ZERO_OBJECT(pMP3);
64485 pMP3->format = ma_format_f32; /* f32 by default. */
64486
64487 if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) {
64488 pMP3->format = pConfig->preferredFormat;
64489 } else {
64490 /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
64491 }
64492
64493 dataSourceConfig = ma_data_source_config_init();
64494 dataSourceConfig.vtable = &g_ma_mp3_ds_vtable;
64495
64496 result = ma_data_source_init(&dataSourceConfig, &pMP3->ds);
64497 if (result != MA_SUCCESS) {
64498 return result; /* Failed to initialize the base data source. */
64499 }
64500
64501 return MA_SUCCESS;
64502}
64503
64504static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks)
64505{
64506 ma_bool32 mp3Result;
64507 ma_uint32 seekPointCount = 0;
64508 ma_dr_mp3_seek_point* pSeekPoints = NULL;
64509
64510 MA_ASSERT(pMP3 != NULL);
64511 MA_ASSERT(pConfig != NULL);
64512
64513 seekPointCount = pConfig->seekPointCount;
64514 if (seekPointCount > 0) {
64515 pSeekPoints = (ma_dr_mp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks);
64516 if (pSeekPoints == NULL) {
64517 return MA_OUT_OF_MEMORY;
64518 }
64519 }
64520
64521 mp3Result = ma_dr_mp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints);
64522 if (mp3Result != MA_TRUE) {
64523 ma_free(pSeekPoints, pAllocationCallbacks);
64524 return MA_ERROR;
64525 }
64526
64527 mp3Result = ma_dr_mp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints);
64528 if (mp3Result != MA_TRUE) {
64529 ma_free(pSeekPoints, pAllocationCallbacks);
64530 return MA_ERROR;
64531 }
64532
64533 pMP3->seekPointCount = seekPointCount;
64534 pMP3->pSeekPoints = pSeekPoints;
64535
64536 return MA_SUCCESS;
64537}
64538
64539static ma_result ma_mp3_post_init(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks)
64540{
64541 ma_result result;
64542
64543 result = ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks);
64544 if (result != MA_SUCCESS) {
64545 return result;
64546 }
64547
64548 return MA_SUCCESS;
64549}
64550
64551MA_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)
64552{
64553 ma_result result;
64554
64555 result = ma_mp3_init_internal(pConfig, pMP3);
64556 if (result != MA_SUCCESS) {
64557 return result;
64558 }
64559
64560 if (onRead == NULL || onSeek == NULL) {
64561 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
64562 }
64563
64564 pMP3->onRead = onRead;
64565 pMP3->onSeek = onSeek;
64566 pMP3->onTell = onTell;
64567 pMP3->pReadSeekTellUserData = pReadSeekTellUserData;
64568
64569 #if !defined(MA_NO_MP3)
64570 {
64571 ma_bool32 mp3Result;
64572
64573 mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, ma_mp3_dr_callback__tell, NULL, pMP3, pAllocationCallbacks);
64574 if (mp3Result != MA_TRUE) {
64575 return MA_INVALID_FILE;
64576 }
64577
64578 ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks);
64579
64580 return MA_SUCCESS;
64581 }
64582 #else
64583 {
64584 /* mp3 is disabled. */
64585 (void)pAllocationCallbacks;
64586 return MA_NOT_IMPLEMENTED;
64587 }
64588 #endif
64589}
64590
64591MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3)
64592{
64593 ma_result result;
64594
64595 result = ma_mp3_init_internal(pConfig, pMP3);
64596 if (result != MA_SUCCESS) {
64597 return result;
64598 }
64599
64600 #if !defined(MA_NO_MP3)
64601 {
64602 ma_bool32 mp3Result;
64603
64604 mp3Result = ma_dr_mp3_init_file(&pMP3->dr, pFilePath, pAllocationCallbacks);
64605 if (mp3Result != MA_TRUE) {
64606 return MA_INVALID_FILE;
64607 }
64608
64609 ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks);
64610
64611 return MA_SUCCESS;
64612 }
64613 #else
64614 {
64615 /* mp3 is disabled. */
64616 (void)pFilePath;
64617 (void)pAllocationCallbacks;
64618 return MA_NOT_IMPLEMENTED;
64619 }
64620 #endif
64621}
64622
64623MA_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)
64624{
64625 ma_result result;
64626
64627 result = ma_mp3_init_internal(pConfig, pMP3);
64628 if (result != MA_SUCCESS) {
64629 return result;
64630 }
64631
64632 #if !defined(MA_NO_MP3)
64633 {
64634 ma_bool32 mp3Result;
64635
64636 mp3Result = ma_dr_mp3_init_file_w(&pMP3->dr, pFilePath, pAllocationCallbacks);
64637 if (mp3Result != MA_TRUE) {
64638 return MA_INVALID_FILE;
64639 }
64640
64641 ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks);
64642
64643 return MA_SUCCESS;
64644 }
64645 #else
64646 {
64647 /* mp3 is disabled. */
64648 (void)pFilePath;
64649 (void)pAllocationCallbacks;
64650 return MA_NOT_IMPLEMENTED;
64651 }
64652 #endif
64653}
64654
64655MA_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)
64656{
64657 ma_result result;
64658
64659 result = ma_mp3_init_internal(pConfig, pMP3);
64660 if (result != MA_SUCCESS) {
64661 return result;
64662 }
64663
64664 #if !defined(MA_NO_MP3)
64665 {
64666 ma_bool32 mp3Result;
64667
64668 mp3Result = ma_dr_mp3_init_memory(&pMP3->dr, pData, dataSize, pAllocationCallbacks);
64669 if (mp3Result != MA_TRUE) {
64670 return MA_INVALID_FILE;
64671 }
64672
64673 ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks);
64674
64675 return MA_SUCCESS;
64676 }
64677 #else
64678 {
64679 /* mp3 is disabled. */
64680 (void)pData;
64681 (void)dataSize;
64682 (void)pAllocationCallbacks;
64683 return MA_NOT_IMPLEMENTED;
64684 }
64685 #endif
64686}
64687
64688MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks)
64689{
64690 if (pMP3 == NULL) {
64691 return;
64692 }
64693
64694 #if !defined(MA_NO_MP3)
64695 {
64696 ma_dr_mp3_uninit(&pMP3->dr);
64697 }
64698 #else
64699 {
64700 /* mp3 is disabled. Should never hit this since initialization would have failed. */
64701 MA_ASSERT(MA_FALSE);
64702 }
64703 #endif
64704
64705 /* Seek points need to be freed after the MP3 decoder has been uninitialized to ensure they're no longer being referenced. */
64706 ma_free(pMP3->pSeekPoints, pAllocationCallbacks);
64707
64708 ma_data_source_uninit(&pMP3->ds);
64709}
64710
64711MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
64712{
64713 if (pFramesRead != NULL) {
64714 *pFramesRead = 0;
64715 }
64716
64717 if (frameCount == 0) {
64718 return MA_INVALID_ARGS;
64719 }
64720
64721 if (pMP3 == NULL) {
64722 return MA_INVALID_ARGS;
64723 }
64724
64725 #if !defined(MA_NO_MP3)
64726 {
64727 /* We always use floating point format. */
64728 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
64729 ma_uint64 totalFramesRead = 0;
64730 ma_format format;
64731
64732 ma_mp3_get_data_format(pMP3, &format, NULL, NULL, NULL, 0);
64733
64734 switch (format)
64735 {
64736 case ma_format_f32:
64737 {
64738 totalFramesRead = ma_dr_mp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut);
64739 } break;
64740
64741 case ma_format_s16:
64742 {
64743 totalFramesRead = ma_dr_mp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (ma_int16*)pFramesOut);
64744 } break;
64745
64746 case ma_format_u8:
64747 case ma_format_s24:
64748 case ma_format_s32:
64749 case ma_format_unknown:
64750 default:
64751 {
64752 return MA_INVALID_OPERATION;
64753 };
64754 }
64755
64756 /* In the future we'll update ma_dr_mp3 to return MA_AT_END for us. */
64757 if (totalFramesRead == 0) {
64758 result = MA_AT_END;
64759 }
64760
64761 if (pFramesRead != NULL) {
64762 *pFramesRead = totalFramesRead;
64763 }
64764
64765 return result;
64766 }
64767 #else
64768 {
64769 /* mp3 is disabled. Should never hit this since initialization would have failed. */
64770 MA_ASSERT(MA_FALSE);
64771
64772 (void)pFramesOut;
64773 (void)frameCount;
64774 (void)pFramesRead;
64775
64776 return MA_NOT_IMPLEMENTED;
64777 }
64778 #endif
64779}
64780
64781MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex)
64782{
64783 if (pMP3 == NULL) {
64784 return MA_INVALID_ARGS;
64785 }
64786
64787 #if !defined(MA_NO_MP3)
64788 {
64789 ma_bool32 mp3Result;
64790
64791 mp3Result = ma_dr_mp3_seek_to_pcm_frame(&pMP3->dr, frameIndex);
64792 if (mp3Result != MA_TRUE) {
64793 return MA_ERROR;
64794 }
64795
64796 return MA_SUCCESS;
64797 }
64798 #else
64799 {
64800 /* mp3 is disabled. Should never hit this since initialization would have failed. */
64801 MA_ASSERT(MA_FALSE);
64802
64803 (void)frameIndex;
64804
64805 return MA_NOT_IMPLEMENTED;
64806 }
64807 #endif
64808}
64809
64810MA_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)
64811{
64812 /* Defaults for safety. */
64813 if (pFormat != NULL) {
64814 *pFormat = ma_format_unknown;
64815 }
64816 if (pChannels != NULL) {
64817 *pChannels = 0;
64818 }
64819 if (pSampleRate != NULL) {
64820 *pSampleRate = 0;
64821 }
64822 if (pChannelMap != NULL) {
64823 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
64824 }
64825
64826 if (pMP3 == NULL) {
64827 return MA_INVALID_OPERATION;
64828 }
64829
64830 if (pFormat != NULL) {
64831 *pFormat = pMP3->format;
64832 }
64833
64834 #if !defined(MA_NO_MP3)
64835 {
64836 if (pChannels != NULL) {
64837 *pChannels = pMP3->dr.channels;
64838 }
64839
64840 if (pSampleRate != NULL) {
64841 *pSampleRate = pMP3->dr.sampleRate;
64842 }
64843
64844 if (pChannelMap != NULL) {
64845 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pMP3->dr.channels);
64846 }
64847
64848 return MA_SUCCESS;
64849 }
64850 #else
64851 {
64852 /* mp3 is disabled. Should never hit this since initialization would have failed. */
64853 MA_ASSERT(MA_FALSE);
64854 return MA_NOT_IMPLEMENTED;
64855 }
64856 #endif
64857}
64858
64859MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor)
64860{
64861 if (pCursor == NULL) {
64862 return MA_INVALID_ARGS;
64863 }
64864
64865 *pCursor = 0; /* Safety. */
64866
64867 if (pMP3 == NULL) {
64868 return MA_INVALID_ARGS;
64869 }
64870
64871 #if !defined(MA_NO_MP3)
64872 {
64873 *pCursor = pMP3->dr.currentPCMFrame;
64874
64875 return MA_SUCCESS;
64876 }
64877 #else
64878 {
64879 /* mp3 is disabled. Should never hit this since initialization would have failed. */
64880 MA_ASSERT(MA_FALSE);
64881 return MA_NOT_IMPLEMENTED;
64882 }
64883 #endif
64884}
64885
64886MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength)
64887{
64888 if (pLength == NULL) {
64889 return MA_INVALID_ARGS;
64890 }
64891
64892 *pLength = 0; /* Safety. */
64893
64894 if (pMP3 == NULL) {
64895 return MA_INVALID_ARGS;
64896 }
64897
64898 #if !defined(MA_NO_MP3)
64899 {
64900 *pLength = ma_dr_mp3_get_pcm_frame_count(&pMP3->dr);
64901
64902 return MA_SUCCESS;
64903 }
64904 #else
64905 {
64906 /* mp3 is disabled. Should never hit this since initialization would have failed. */
64907 MA_ASSERT(MA_FALSE);
64908 return MA_NOT_IMPLEMENTED;
64909 }
64910 #endif
64911}
64912
64913
64914static 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)
64915{
64916 ma_result result;
64917 ma_mp3* pMP3;
64918
64919 (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. */
64920
64921 /* For now we're just allocating the decoder backend on the heap. */
64922 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
64923 if (pMP3 == NULL) {
64924 return MA_OUT_OF_MEMORY;
64925 }
64926
64927 result = ma_mp3_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pMP3);
64928 if (result != MA_SUCCESS) {
64929 ma_free(pMP3, pAllocationCallbacks);
64930 return result;
64931 }
64932
64933 *ppBackend = pMP3;
64934
64935 return MA_SUCCESS;
64936}
64937
64938static 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)
64939{
64940 ma_result result;
64941 ma_mp3* pMP3;
64942
64943 (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. */
64944
64945 /* For now we're just allocating the decoder backend on the heap. */
64946 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
64947 if (pMP3 == NULL) {
64948 return MA_OUT_OF_MEMORY;
64949 }
64950
64951 result = ma_mp3_init_file(pFilePath, pConfig, pAllocationCallbacks, pMP3);
64952 if (result != MA_SUCCESS) {
64953 ma_free(pMP3, pAllocationCallbacks);
64954 return result;
64955 }
64956
64957 *ppBackend = pMP3;
64958
64959 return MA_SUCCESS;
64960}
64961
64962static 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)
64963{
64964 ma_result result;
64965 ma_mp3* pMP3;
64966
64967 (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. */
64968
64969 /* For now we're just allocating the decoder backend on the heap. */
64970 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
64971 if (pMP3 == NULL) {
64972 return MA_OUT_OF_MEMORY;
64973 }
64974
64975 result = ma_mp3_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pMP3);
64976 if (result != MA_SUCCESS) {
64977 ma_free(pMP3, pAllocationCallbacks);
64978 return result;
64979 }
64980
64981 *ppBackend = pMP3;
64982
64983 return MA_SUCCESS;
64984}
64985
64986static 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)
64987{
64988 ma_result result;
64989 ma_mp3* pMP3;
64990
64991 (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. */
64992
64993 /* For now we're just allocating the decoder backend on the heap. */
64994 pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
64995 if (pMP3 == NULL) {
64996 return MA_OUT_OF_MEMORY;
64997 }
64998
64999 result = ma_mp3_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pMP3);
65000 if (result != MA_SUCCESS) {
65001 ma_free(pMP3, pAllocationCallbacks);
65002 return result;
65003 }
65004
65005 *ppBackend = pMP3;
65006
65007 return MA_SUCCESS;
65008}
65009
65010static void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
65011{
65012 ma_mp3* pMP3 = (ma_mp3*)pBackend;
65013
65014 (void)pUserData;
65015
65016 ma_mp3_uninit(pMP3, pAllocationCallbacks);
65017 ma_free(pMP3, pAllocationCallbacks);
65018}
65019
65020static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 =
65021{
65022 ma_decoding_backend_init__mp3,
65023 ma_decoding_backend_init_file__mp3,
65024 ma_decoding_backend_init_file_w__mp3,
65025 ma_decoding_backend_init_memory__mp3,
65026 ma_decoding_backend_uninit__mp3
65027};
65028
65029static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
65030{
65031 return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder);
65032}
65033
65034static ma_result ma_decoder_init_mp3_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
65035{
65036 return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder);
65037}
65038
65039static ma_result ma_decoder_init_mp3_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
65040{
65041 return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder);
65042}
65043
65044static ma_result ma_decoder_init_mp3_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
65045{
65046 return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pData, dataSize, pConfig, pDecoder);
65047}
65048#endif /* ma_dr_mp3_h */
65049
65050/* Vorbis */
65051#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
65052#define MA_HAS_VORBIS
65053
65054/* The size in bytes of each chunk of data to read from the Vorbis stream. */
65055#define MA_VORBIS_DATA_CHUNK_SIZE 4096
65056
65057typedef struct
65058{
65059 ma_data_source_base ds;
65060 ma_read_proc onRead;
65061 ma_seek_proc onSeek;
65062 ma_tell_proc onTell;
65063 void* pReadSeekTellUserData;
65064 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. */
65065 ma_format format; /* Only f32 is allowed with stb_vorbis. */
65066 ma_uint32 channels;
65067 ma_uint32 sampleRate;
65068 ma_uint64 cursor;
65069#if !defined(MA_NO_VORBIS)
65070 stb_vorbis* stb;
65071 ma_bool32 usingPushMode;
65072 struct
65073 {
65074 ma_uint8* pData;
65075 size_t dataSize;
65076 size_t dataCapacity;
65077 size_t audioStartOffsetInBytes;
65078 ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */
65079 ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */
65080 float** ppPacketData;
65081 } push;
65082#endif
65083} ma_stbvorbis;
65084
65085MA_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);
65086MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis);
65087MA_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);
65088MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks);
65089MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
65090MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex);
65091MA_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);
65092MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor);
65093MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength);
65094
65095
65096static ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
65097{
65098 return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead);
65099}
65100
65101static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
65102{
65103 return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex);
65104}
65105
65106static 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)
65107{
65108 return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
65109}
65110
65111static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
65112{
65113 return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor);
65114}
65115
65116static ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
65117{
65118 return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength);
65119}
65120
65121static ma_data_source_vtable g_ma_stbvorbis_ds_vtable =
65122{
65123 ma_stbvorbis_ds_read,
65124 ma_stbvorbis_ds_seek,
65125 ma_stbvorbis_ds_get_data_format,
65126 ma_stbvorbis_ds_get_cursor,
65127 ma_stbvorbis_ds_get_length,
65128 NULL, /* onSetLooping */
65129 0
65130};
65131
65132
65133static ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis)
65134{
65135 ma_result result;
65136 ma_data_source_config dataSourceConfig;
65137
65138 (void)pConfig;
65139
65140 if (pVorbis == NULL) {
65141 return MA_INVALID_ARGS;
65142 }
65143
65144 MA_ZERO_OBJECT(pVorbis);
65145 pVorbis->format = ma_format_f32; /* Only supporting f32. */
65146
65147 dataSourceConfig = ma_data_source_config_init();
65148 dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable;
65149
65150 result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds);
65151 if (result != MA_SUCCESS) {
65152 return result; /* Failed to initialize the base data source. */
65153 }
65154
65155 return MA_SUCCESS;
65156}
65157
65158#if !defined(MA_NO_VORBIS)
65159static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis)
65160{
65161 stb_vorbis_info info;
65162
65163 MA_ASSERT(pVorbis != NULL);
65164
65165 info = stb_vorbis_get_info(pVorbis->stb);
65166
65167 pVorbis->channels = info.channels;
65168 pVorbis->sampleRate = info.sample_rate;
65169
65170 return MA_SUCCESS;
65171}
65172
65173static ma_result ma_stbvorbis_init_internal_decoder_push(ma_stbvorbis* pVorbis)
65174{
65175 ma_result result;
65176 stb_vorbis* stb;
65177 size_t dataSize = 0;
65178 size_t dataCapacity = 0;
65179 ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */
65180
65181 for (;;) {
65182 int vorbisError;
65183 int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */
65184 size_t bytesRead;
65185 ma_uint8* pNewData;
65186
65187 /* Allocate memory for the new chunk. */
65188 dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE;
65189 pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, &pVorbis->allocationCallbacks);
65190 if (pNewData == NULL) {
65191 ma_free(pData, &pVorbis->allocationCallbacks);
65192 return MA_OUT_OF_MEMORY;
65193 }
65194
65195 pData = pNewData;
65196
65197 /* Read in the next chunk. */
65198 result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead);
65199 dataSize += bytesRead;
65200
65201 if (result != MA_SUCCESS) {
65202 ma_free(pData, &pVorbis->allocationCallbacks);
65203 return result;
65204 }
65205
65206 /* We have a maximum of 31 bits with stb_vorbis. */
65207 if (dataSize > INT_MAX) {
65208 ma_free(pData, &pVorbis->allocationCallbacks);
65209 return MA_TOO_BIG;
65210 }
65211
65212 stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL);
65213 if (stb != NULL) {
65214 /*
65215 Successfully opened the Vorbis decoder. We might have some leftover unprocessed
65216 data so we'll need to move that down to the front.
65217 */
65218 dataSize -= (size_t)consumedDataSize; /* Consume the data. */
65219 MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize);
65220
65221 /*
65222 We need to track the start point so we can seek back to the start of the audio
65223 data when seeking.
65224 */
65225 pVorbis->push.audioStartOffsetInBytes = consumedDataSize;
65226
65227 break;
65228 } else {
65229 /* Failed to open the decoder. */
65230 if (vorbisError == VORBIS_need_more_data) {
65231 continue;
65232 } else {
65233 ma_free(pData, &pVorbis->allocationCallbacks);
65234 return MA_ERROR; /* Failed to open the stb_vorbis decoder. */
65235 }
65236 }
65237 }
65238
65239 MA_ASSERT(stb != NULL);
65240 pVorbis->stb = stb;
65241 pVorbis->push.pData = pData;
65242 pVorbis->push.dataSize = dataSize;
65243 pVorbis->push.dataCapacity = dataCapacity;
65244
65245 return MA_SUCCESS;
65246}
65247#endif
65248
65249MA_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)
65250{
65251 ma_result result;
65252
65253 result = ma_stbvorbis_init_internal(pConfig, pVorbis);
65254 if (result != MA_SUCCESS) {
65255 return result;
65256 }
65257
65258 if (onRead == NULL || onSeek == NULL) {
65259 return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
65260 }
65261
65262 pVorbis->onRead = onRead;
65263 pVorbis->onSeek = onSeek;
65264 pVorbis->onTell = onTell;
65265 pVorbis->pReadSeekTellUserData = pReadSeekTellUserData;
65266 ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks);
65267
65268 #if !defined(MA_NO_VORBIS)
65269 {
65270 /*
65271 stb_vorbis lacks a callback based API for its pulling API which means we're stuck with the
65272 pushing API. In order for us to be able to successfully initialize the decoder we need to
65273 supply it with enough data. We need to keep loading data until we have enough.
65274 */
65275 result = ma_stbvorbis_init_internal_decoder_push(pVorbis);
65276 if (result != MA_SUCCESS) {
65277 return result;
65278 }
65279
65280 pVorbis->usingPushMode = MA_TRUE;
65281
65282 result = ma_stbvorbis_post_init(pVorbis);
65283 if (result != MA_SUCCESS) {
65284 stb_vorbis_close(pVorbis->stb);
65285 ma_free(pVorbis->push.pData, pAllocationCallbacks);
65286 return result;
65287 }
65288
65289 return MA_SUCCESS;
65290 }
65291 #else
65292 {
65293 /* vorbis is disabled. */
65294 (void)pAllocationCallbacks;
65295 return MA_NOT_IMPLEMENTED;
65296 }
65297 #endif
65298}
65299
65300MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis)
65301{
65302 ma_result result;
65303
65304 result = ma_stbvorbis_init_internal(pConfig, pVorbis);
65305 if (result != MA_SUCCESS) {
65306 return result;
65307 }
65308
65309 #if !defined(MA_NO_VORBIS)
65310 {
65311 (void)pAllocationCallbacks; /* Don't know how to make use of this with stb_vorbis. */
65312
65313 /* We can use stb_vorbis' pull mode for file based streams. */
65314 pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL);
65315 if (pVorbis->stb == NULL) {
65316 return MA_INVALID_FILE;
65317 }
65318
65319 pVorbis->usingPushMode = MA_FALSE;
65320
65321 result = ma_stbvorbis_post_init(pVorbis);
65322 if (result != MA_SUCCESS) {
65323 stb_vorbis_close(pVorbis->stb);
65324 return result;
65325 }
65326
65327 return MA_SUCCESS;
65328 }
65329 #else
65330 {
65331 /* vorbis is disabled. */
65332 (void)pFilePath;
65333 (void)pAllocationCallbacks;
65334 return MA_NOT_IMPLEMENTED;
65335 }
65336 #endif
65337}
65338
65339MA_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)
65340{
65341 ma_result result;
65342
65343 result = ma_stbvorbis_init_internal(pConfig, pVorbis);
65344 if (result != MA_SUCCESS) {
65345 return result;
65346 }
65347
65348 #if !defined(MA_NO_VORBIS)
65349 {
65350 (void)pAllocationCallbacks;
65351
65352 /* stb_vorbis uses an int as its size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */
65353 if (dataSize > INT_MAX) {
65354 return MA_TOO_BIG;
65355 }
65356
65357 pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL);
65358 if (pVorbis->stb == NULL) {
65359 return MA_INVALID_FILE;
65360 }
65361
65362 pVorbis->usingPushMode = MA_FALSE;
65363
65364 result = ma_stbvorbis_post_init(pVorbis);
65365 if (result != MA_SUCCESS) {
65366 stb_vorbis_close(pVorbis->stb);
65367 return result;
65368 }
65369
65370 return MA_SUCCESS;
65371 }
65372 #else
65373 {
65374 /* vorbis is disabled. */
65375 (void)pData;
65376 (void)dataSize;
65377 (void)pAllocationCallbacks;
65378 return MA_NOT_IMPLEMENTED;
65379 }
65380 #endif
65381}
65382
65383MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks)
65384{
65385 if (pVorbis == NULL) {
65386 return;
65387 }
65388
65389 #if !defined(MA_NO_VORBIS)
65390 {
65391 stb_vorbis_close(pVorbis->stb);
65392
65393 /* We'll have to clear some memory if we're using push mode. */
65394 if (pVorbis->usingPushMode) {
65395 ma_free(pVorbis->push.pData, pAllocationCallbacks);
65396 }
65397 }
65398 #else
65399 {
65400 /* vorbis is disabled. Should never hit this since initialization would have failed. */
65401 MA_ASSERT(MA_FALSE);
65402 }
65403 #endif
65404
65405 ma_data_source_uninit(&pVorbis->ds);
65406}
65407
65408MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
65409{
65410 if (pFramesRead != NULL) {
65411 *pFramesRead = 0;
65412 }
65413
65414 if (frameCount == 0) {
65415 return MA_INVALID_ARGS;
65416 }
65417
65418 if (pVorbis == NULL) {
65419 return MA_INVALID_ARGS;
65420 }
65421
65422 #if !defined(MA_NO_VORBIS)
65423 {
65424 /* We always use floating point format. */
65425 ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
65426 ma_uint64 totalFramesRead = 0;
65427 ma_format format;
65428 ma_uint32 channels;
65429
65430 ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0);
65431
65432 if (format == ma_format_f32) {
65433 /* We read differently depending on whether or not we're using push mode. */
65434 if (pVorbis->usingPushMode) {
65435 /* Push mode. This is the complex case. */
65436 float* pFramesOutF32 = (float*)pFramesOut;
65437
65438 while (totalFramesRead < frameCount) {
65439 /* The first thing to do is read from any already-cached frames. */
65440 ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */
65441
65442 /* The output pointer can be null in which case we just treat it as a seek. */
65443 if (pFramesOut != NULL) {
65444 ma_uint64 iFrame;
65445 for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) {
65446 ma_uint32 iChannel;
65447 for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) {
65448 pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame];
65449 }
65450
65451 pFramesOutF32 += pVorbis->channels;
65452 }
65453 }
65454
65455 /* Update pointers and counters. */
65456 pVorbis->push.framesConsumed += framesToReadFromCache;
65457 pVorbis->push.framesRemaining -= framesToReadFromCache;
65458 totalFramesRead += framesToReadFromCache;
65459
65460 /* Don't bother reading any more frames right now if we've just finished loading. */
65461 if (totalFramesRead == frameCount) {
65462 break;
65463 }
65464
65465 MA_ASSERT(pVorbis->push.framesRemaining == 0);
65466
65467 /* Getting here means we've run out of cached frames. We'll need to load some more. */
65468 for (;;) {
65469 int samplesRead = 0;
65470 int consumedDataSize;
65471
65472 /* We need to case dataSize to an int, so make sure we can do it safely. */
65473 if (pVorbis->push.dataSize > INT_MAX) {
65474 break; /* Too big. */
65475 }
65476
65477 consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead);
65478 if (consumedDataSize != 0) {
65479 /* Successfully decoded a Vorbis frame. Consume the data. */
65480 pVorbis->push.dataSize -= (size_t)consumedDataSize;
65481 MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize);
65482
65483 pVorbis->push.framesConsumed = 0;
65484 pVorbis->push.framesRemaining = samplesRead;
65485
65486 break;
65487 } else {
65488 /* Not enough data. Read more. */
65489 size_t bytesRead;
65490
65491 /* Expand the data buffer if necessary. */
65492 if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) {
65493 size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE;
65494 ma_uint8* pNewData;
65495
65496 pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks);
65497 if (pNewData == NULL) {
65498 result = MA_OUT_OF_MEMORY;
65499 break;
65500 }
65501
65502 pVorbis->push.pData = pNewData;
65503 pVorbis->push.dataCapacity = newCap;
65504 }
65505
65506 /* We should have enough room to load some data. */
65507 result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead);
65508 pVorbis->push.dataSize += bytesRead;
65509
65510 if (result != MA_SUCCESS) {
65511 break; /* Failed to read any data. Get out. */
65512 }
65513 }
65514 }
65515
65516 /* If we don't have a success code at this point it means we've encountered an error or the end of the file has been reached (probably the latter). */
65517 if (result != MA_SUCCESS) {
65518 break;
65519 }
65520 }
65521 } else {
65522 /* 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. */
65523 while (totalFramesRead < frameCount) {
65524 ma_uint64 framesRemaining = (frameCount - totalFramesRead);
65525 int framesRead;
65526
65527 if (framesRemaining > INT_MAX) {
65528 framesRemaining = INT_MAX;
65529 }
65530
65531 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. */
65532 totalFramesRead += framesRead;
65533
65534 if (framesRead < (int)framesRemaining) {
65535 break; /* Nothing left to read. Get out. */
65536 }
65537 }
65538 }
65539 } else {
65540 result = MA_INVALID_ARGS;
65541 }
65542
65543 pVorbis->cursor += totalFramesRead;
65544
65545 if (totalFramesRead == 0) {
65546 result = MA_AT_END;
65547 }
65548
65549 if (pFramesRead != NULL) {
65550 *pFramesRead = totalFramesRead;
65551 }
65552
65553 if (result == MA_SUCCESS && totalFramesRead == 0) {
65554 result = MA_AT_END;
65555 }
65556
65557 return result;
65558 }
65559 #else
65560 {
65561 /* vorbis is disabled. Should never hit this since initialization would have failed. */
65562 MA_ASSERT(MA_FALSE);
65563
65564 (void)pFramesOut;
65565 (void)frameCount;
65566 (void)pFramesRead;
65567
65568 return MA_NOT_IMPLEMENTED;
65569 }
65570 #endif
65571}
65572
65573MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex)
65574{
65575 if (pVorbis == NULL) {
65576 return MA_INVALID_ARGS;
65577 }
65578
65579 #if !defined(MA_NO_VORBIS)
65580 {
65581 /* Different seeking methods depending on whether or not we're using push mode. */
65582 if (pVorbis->usingPushMode) {
65583 /* Push mode. This is the complex case. */
65584 ma_result result;
65585 float buffer[4096];
65586
65587 /* If we're seeking backwards, we need to seek back to the start and then brute-force forward. */
65588 if (frameIndex < pVorbis->cursor) {
65589 if (frameIndex > 0x7FFFFFFF) {
65590 return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */
65591 }
65592
65593 /*
65594 This is wildly inefficient due to me having trouble getting sample exact seeking working
65595 robustly with stb_vorbis_flush_pushdata(). The only way I can think to make this work
65596 perfectly is to reinitialize the decoder. Note that we only enter this path when seeking
65597 backwards. This will hopefully be removed once we get our own Vorbis decoder implemented.
65598 */
65599 stb_vorbis_close(pVorbis->stb);
65600 ma_free(pVorbis->push.pData, &pVorbis->allocationCallbacks);
65601
65602 MA_ZERO_OBJECT(&pVorbis->push);
65603
65604 /* Seek to the start of the file. */
65605 result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start);
65606 if (result != MA_SUCCESS) {
65607 return result;
65608 }
65609
65610 result = ma_stbvorbis_init_internal_decoder_push(pVorbis);
65611 if (result != MA_SUCCESS) {
65612 return result;
65613 }
65614
65615 /* At this point we should be sitting on the first frame. */
65616 pVorbis->cursor = 0;
65617 }
65618
65619 /* We're just brute-forcing this for now. */
65620 while (pVorbis->cursor < frameIndex) {
65621 ma_uint64 framesRead;
65622 ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels;
65623 if (framesToRead > (frameIndex - pVorbis->cursor)) {
65624 framesToRead = (frameIndex - pVorbis->cursor);
65625 }
65626
65627 result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead);
65628 if (result != MA_SUCCESS) {
65629 return result;
65630 }
65631 }
65632 } else {
65633 /* Pull mode. This is the simple case. */
65634 int vorbisResult;
65635
65636 if (frameIndex > UINT_MAX) {
65637 return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */
65638 }
65639
65640 vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex); /* Safe cast. */
65641 if (vorbisResult == 0) {
65642 return MA_ERROR; /* See failed. */
65643 }
65644
65645 pVorbis->cursor = frameIndex;
65646 }
65647
65648 return MA_SUCCESS;
65649 }
65650 #else
65651 {
65652 /* vorbis is disabled. Should never hit this since initialization would have failed. */
65653 MA_ASSERT(MA_FALSE);
65654
65655 (void)frameIndex;
65656
65657 return MA_NOT_IMPLEMENTED;
65658 }
65659 #endif
65660}
65661
65662MA_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)
65663{
65664 /* Defaults for safety. */
65665 if (pFormat != NULL) {
65666 *pFormat = ma_format_unknown;
65667 }
65668 if (pChannels != NULL) {
65669 *pChannels = 0;
65670 }
65671 if (pSampleRate != NULL) {
65672 *pSampleRate = 0;
65673 }
65674 if (pChannelMap != NULL) {
65675 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
65676 }
65677
65678 if (pVorbis == NULL) {
65679 return MA_INVALID_OPERATION;
65680 }
65681
65682 if (pFormat != NULL) {
65683 *pFormat = pVorbis->format;
65684 }
65685
65686 #if !defined(MA_NO_VORBIS)
65687 {
65688 if (pChannels != NULL) {
65689 *pChannels = pVorbis->channels;
65690 }
65691
65692 if (pSampleRate != NULL) {
65693 *pSampleRate = pVorbis->sampleRate;
65694 }
65695
65696 if (pChannelMap != NULL) {
65697 ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pVorbis->channels);
65698 }
65699
65700 return MA_SUCCESS;
65701 }
65702 #else
65703 {
65704 /* vorbis is disabled. Should never hit this since initialization would have failed. */
65705 MA_ASSERT(MA_FALSE);
65706 return MA_NOT_IMPLEMENTED;
65707 }
65708 #endif
65709}
65710
65711MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor)
65712{
65713 if (pCursor == NULL) {
65714 return MA_INVALID_ARGS;
65715 }
65716
65717 *pCursor = 0; /* Safety. */
65718
65719 if (pVorbis == NULL) {
65720 return MA_INVALID_ARGS;
65721 }
65722
65723 #if !defined(MA_NO_VORBIS)
65724 {
65725 *pCursor = pVorbis->cursor;
65726
65727 return MA_SUCCESS;
65728 }
65729 #else
65730 {
65731 /* vorbis is disabled. Should never hit this since initialization would have failed. */
65732 MA_ASSERT(MA_FALSE);
65733 return MA_NOT_IMPLEMENTED;
65734 }
65735 #endif
65736}
65737
65738MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength)
65739{
65740 if (pLength == NULL) {
65741 return MA_INVALID_ARGS;
65742 }
65743
65744 *pLength = 0; /* Safety. */
65745
65746 if (pVorbis == NULL) {
65747 return MA_INVALID_ARGS;
65748 }
65749
65750 #if !defined(MA_NO_VORBIS)
65751 {
65752 if (pVorbis->usingPushMode) {
65753 *pLength = 0; /* I don't know of a good way to determine this reliably with stb_vorbis and push mode. */
65754 } else {
65755 *pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb);
65756 }
65757
65758 return MA_SUCCESS;
65759 }
65760 #else
65761 {
65762 /* vorbis is disabled. Should never hit this since initialization would have failed. */
65763 MA_ASSERT(MA_FALSE);
65764 return MA_NOT_IMPLEMENTED;
65765 }
65766 #endif
65767}
65768
65769
65770static 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)
65771{
65772 ma_result result;
65773 ma_stbvorbis* pVorbis;
65774
65775 (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. */
65776
65777 /* For now we're just allocating the decoder backend on the heap. */
65778 pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
65779 if (pVorbis == NULL) {
65780 return MA_OUT_OF_MEMORY;
65781 }
65782
65783 result = ma_stbvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis);
65784 if (result != MA_SUCCESS) {
65785 ma_free(pVorbis, pAllocationCallbacks);
65786 return result;
65787 }
65788
65789 *ppBackend = pVorbis;
65790
65791 return MA_SUCCESS;
65792}
65793
65794static 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)
65795{
65796 ma_result result;
65797 ma_stbvorbis* pVorbis;
65798
65799 (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. */
65800
65801 /* For now we're just allocating the decoder backend on the heap. */
65802 pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
65803 if (pVorbis == NULL) {
65804 return MA_OUT_OF_MEMORY;
65805 }
65806
65807 result = ma_stbvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis);
65808 if (result != MA_SUCCESS) {
65809 ma_free(pVorbis, pAllocationCallbacks);
65810 return result;
65811 }
65812
65813 *ppBackend = pVorbis;
65814
65815 return MA_SUCCESS;
65816}
65817
65818static 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)
65819{
65820 ma_result result;
65821 ma_stbvorbis* pVorbis;
65822
65823 (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. */
65824
65825 /* For now we're just allocating the decoder backend on the heap. */
65826 pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
65827 if (pVorbis == NULL) {
65828 return MA_OUT_OF_MEMORY;
65829 }
65830
65831 result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis);
65832 if (result != MA_SUCCESS) {
65833 ma_free(pVorbis, pAllocationCallbacks);
65834 return result;
65835 }
65836
65837 *ppBackend = pVorbis;
65838
65839 return MA_SUCCESS;
65840}
65841
65842static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
65843{
65844 ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend;
65845
65846 (void)pUserData;
65847
65848 ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks);
65849 ma_free(pVorbis, pAllocationCallbacks);
65850}
65851
65852static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis =
65853{
65854 ma_decoding_backend_init__stbvorbis,
65855 ma_decoding_backend_init_file__stbvorbis,
65856 NULL, /* onInitFileW() */
65857 ma_decoding_backend_init_memory__stbvorbis,
65858 ma_decoding_backend_uninit__stbvorbis
65859};
65860
65861static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
65862{
65863 return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder);
65864}
65865
65866static ma_result ma_decoder_init_vorbis_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
65867{
65868 return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder);
65869}
65870
65871static ma_result ma_decoder_init_vorbis_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
65872{
65873 return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder);
65874}
65875
65876static ma_result ma_decoder_init_vorbis_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
65877{
65878 return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pData, dataSize, pConfig, pDecoder);
65879}
65880#endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */
65881
65882
65883
65884static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
65885{
65886 MA_ASSERT(pDecoder != NULL);
65887
65888 if (pConfig != NULL) {
65889 return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks);
65890 } else {
65891 pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default();
65892 return MA_SUCCESS;
65893 }
65894}
65895
65896static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
65897{
65898 return ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount, pFramesRead);
65899}
65900
65901static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
65902{
65903 return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex);
65904}
65905
65906static 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)
65907{
65908 return ma_decoder_get_data_format((ma_decoder*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
65909}
65910
65911static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
65912{
65913 return ma_decoder_get_cursor_in_pcm_frames((ma_decoder*)pDataSource, pCursor);
65914}
65915
65916static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
65917{
65918 return ma_decoder_get_length_in_pcm_frames((ma_decoder*)pDataSource, pLength);
65919}
65920
65921static ma_data_source_vtable g_ma_decoder_data_source_vtable =
65922{
65923 ma_decoder__data_source_on_read,
65924 ma_decoder__data_source_on_seek,
65925 ma_decoder__data_source_on_get_data_format,
65926 ma_decoder__data_source_on_get_cursor,
65927 ma_decoder__data_source_on_get_length,
65928 NULL, /* onSetLooping */
65929 0
65930};
65931
65932static 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)
65933{
65934 ma_result result;
65935 ma_data_source_config dataSourceConfig;
65936
65937 MA_ASSERT(pConfig != NULL);
65938
65939 if (pDecoder == NULL) {
65940 return MA_INVALID_ARGS;
65941 }
65942
65943 MA_ZERO_OBJECT(pDecoder);
65944
65945 dataSourceConfig = ma_data_source_config_init();
65946 dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable;
65947
65948 result = ma_data_source_init(&dataSourceConfig, &pDecoder->ds);
65949 if (result != MA_SUCCESS) {
65950 return result;
65951 }
65952
65953 pDecoder->onRead = onRead;
65954 pDecoder->onSeek = onSeek;
65955 pDecoder->onTell = onTell;
65956 pDecoder->pUserData = pUserData;
65957
65958 result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder);
65959 if (result != MA_SUCCESS) {
65960 ma_data_source_uninit(&pDecoder->ds);
65961 return result;
65962 }
65963
65964 return MA_SUCCESS;
65965}
65966
65967static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
65968{
65969 ma_result result;
65970
65971 result = ma_decoder__init_data_converter(pDecoder, pConfig);
65972
65973 /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */
65974 if (result != MA_SUCCESS) {
65975 ma_decoder_uninit(pDecoder);
65976 return result;
65977 }
65978
65979 return result;
65980}
65981
65982
65983static 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)
65984{
65985 ma_result result = MA_NO_BACKEND;
65986
65987 MA_ASSERT(pConfig != NULL);
65988 MA_ASSERT(pDecoder != NULL);
65989
65990 /* Silence some warnings in the case that we don't have any decoder backends enabled. */
65991 (void)onRead;
65992 (void)onSeek;
65993 (void)pUserData;
65994
65995
65996 /* If we've specified a specific encoding type, try that first. */
65998 #ifdef MA_HAS_WAV
65999 if (pConfig->encodingFormat == ma_encoding_format_wav) {
66000 result = ma_decoder_init_wav__internal(pConfig, pDecoder);
66001 }
66002 #endif
66003 #ifdef MA_HAS_FLAC
66004 if (pConfig->encodingFormat == ma_encoding_format_flac) {
66005 result = ma_decoder_init_flac__internal(pConfig, pDecoder);
66006 }
66007 #endif
66008 #ifdef MA_HAS_MP3
66009 if (pConfig->encodingFormat == ma_encoding_format_mp3) {
66010 result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
66011 }
66012 #endif
66013 #ifdef MA_HAS_VORBIS
66014 if (pConfig->encodingFormat == ma_encoding_format_vorbis) {
66015 result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
66016 }
66017 #endif
66018
66019 /* If we weren't able to initialize the decoder, seek back to the start to give the next attempts a clean start. */
66020 if (result != MA_SUCCESS) {
66021 onSeek(pDecoder, 0, ma_seek_origin_start);
66022 }
66023 }
66024
66025 if (result != MA_SUCCESS) {
66026 /* Getting here means we couldn't load a specific decoding backend based on the encoding format. */
66027
66028 /*
66029 We use trial and error to open a decoder. We prioritize custom decoders so that if they
66030 implement the same encoding format they take priority over the built-in decoders.
66031 */
66032 if (result != MA_SUCCESS) {
66033 result = ma_decoder_init_custom__internal(pConfig, pDecoder);
66034 if (result != MA_SUCCESS) {
66035 onSeek(pDecoder, 0, ma_seek_origin_start);
66036 }
66037 }
66038
66039 /*
66040 If we get to this point and we still haven't found a decoder, and the caller has requested a
66041 specific encoding format, there's no hope for it. Abort.
66042 */
66044 return MA_NO_BACKEND;
66045 }
66046
66047 #ifdef MA_HAS_WAV
66048 if (result != MA_SUCCESS) {
66049 result = ma_decoder_init_wav__internal(pConfig, pDecoder);
66050 if (result != MA_SUCCESS) {
66051 onSeek(pDecoder, 0, ma_seek_origin_start);
66052 }
66053 }
66054 #endif
66055 #ifdef MA_HAS_FLAC
66056 if (result != MA_SUCCESS) {
66057 result = ma_decoder_init_flac__internal(pConfig, pDecoder);
66058 if (result != MA_SUCCESS) {
66059 onSeek(pDecoder, 0, ma_seek_origin_start);
66060 }
66061 }
66062 #endif
66063 #ifdef MA_HAS_MP3
66064 if (result != MA_SUCCESS) {
66065 result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
66066 if (result != MA_SUCCESS) {
66067 onSeek(pDecoder, 0, ma_seek_origin_start);
66068 }
66069 }
66070 #endif
66071 #ifdef MA_HAS_VORBIS
66072 if (result != MA_SUCCESS) {
66073 result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
66074 if (result != MA_SUCCESS) {
66075 onSeek(pDecoder, 0, ma_seek_origin_start);
66076 }
66077 }
66078 #endif
66079 }
66080
66081 if (result != MA_SUCCESS) {
66082 return result;
66083 }
66084
66085 return ma_decoder__postinit(pConfig, pDecoder);
66086}
66087
66088MA_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)
66089{
66090 ma_decoder_config config;
66091 ma_result result;
66092
66093 config = ma_decoder_config_init_copy(pConfig);
66094
66095 result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder);
66096 if (result != MA_SUCCESS) {
66097 return result;
66098 }
66099
66100 return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder);
66101}
66102
66103
66104static ma_result ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
66105{
66106 size_t bytesRemaining;
66107
66108 MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos);
66109
66110 if (pBytesRead != NULL) {
66111 *pBytesRead = 0;
66112 }
66113
66114 bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos;
66115 if (bytesToRead > bytesRemaining) {
66116 bytesToRead = bytesRemaining;
66117 }
66118
66119 if (bytesRemaining == 0) {
66120 return MA_AT_END;
66121 }
66122
66123 if (bytesToRead > 0) {
66124 MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead);
66125 pDecoder->data.memory.currentReadPos += bytesToRead;
66126 }
66127
66128 if (pBytesRead != NULL) {
66129 *pBytesRead = bytesToRead;
66130 }
66131
66132 return MA_SUCCESS;
66133}
66134
66135static ma_result ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin)
66136{
66137 if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) {
66138 return MA_BAD_SEEK;
66139 }
66140
66141 if (origin == ma_seek_origin_current) {
66142 if (byteOffset > 0) {
66143 if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) {
66144 byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos); /* Trying to seek too far forward. */
66145 }
66146
66147 pDecoder->data.memory.currentReadPos += (size_t)byteOffset;
66148 } else {
66149 if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) {
66150 byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos; /* Trying to seek too far backwards. */
66151 }
66152
66153 pDecoder->data.memory.currentReadPos -= (size_t)-byteOffset;
66154 }
66155 } else {
66156 if (origin == ma_seek_origin_end) {
66157 if (byteOffset < 0) {
66158 byteOffset = -byteOffset;
66159 }
66160
66161 if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) {
66162 pDecoder->data.memory.currentReadPos = 0; /* Trying to seek too far back. */
66163 } else {
66164 pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - (size_t)byteOffset;
66165 }
66166 } else {
66167 if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) {
66168 pDecoder->data.memory.currentReadPos = (size_t)byteOffset;
66169 } else {
66170 pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize; /* Trying to seek too far forward. */
66171 }
66172 }
66173 }
66174
66175 return MA_SUCCESS;
66176}
66177
66178static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor)
66179{
66180 MA_ASSERT(pDecoder != NULL);
66181 MA_ASSERT(pCursor != NULL);
66182
66183 *pCursor = (ma_int64)pDecoder->data.memory.currentReadPos;
66184
66185 return MA_SUCCESS;
66186}
66187
66188static ma_result ma_decoder__preinit_memory_wrapper(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
66189{
66190 ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder);
66191 if (result != MA_SUCCESS) {
66192 return result;
66193 }
66194
66195 if (pData == NULL || dataSize == 0) {
66196 return MA_INVALID_ARGS;
66197 }
66198
66199 pDecoder->data.memory.pData = (const ma_uint8*)pData;
66200 pDecoder->data.memory.dataSize = dataSize;
66201 pDecoder->data.memory.currentReadPos = 0;
66202
66203 (void)pConfig;
66204 return MA_SUCCESS;
66205}
66206
66207MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
66208{
66209 ma_result result;
66210 ma_decoder_config config;
66211
66212 config = ma_decoder_config_init_copy(pConfig);
66213
66214 result = ma_decoder__preinit(NULL, NULL, NULL, NULL, &config, pDecoder);
66215 if (result != MA_SUCCESS) {
66216 return result;
66217 }
66218
66219 if (pData == NULL || dataSize == 0) {
66220 return MA_INVALID_ARGS;
66221 }
66222
66223 /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */
66224 result = MA_NO_BACKEND;
66225
66227 #ifdef MA_HAS_WAV
66228 if (config.encodingFormat == ma_encoding_format_wav) {
66229 result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder);
66230 }
66231 #endif
66232 #ifdef MA_HAS_FLAC
66234 result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder);
66235 }
66236 #endif
66237 #ifdef MA_HAS_MP3
66238 if (config.encodingFormat == ma_encoding_format_mp3) {
66239 result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder);
66240 }
66241 #endif
66242 #ifdef MA_HAS_VORBIS
66244 result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder);
66245 }
66246 #endif
66247 }
66248
66249 if (result != MA_SUCCESS) {
66250 /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */
66251
66252 /*
66253 We use trial and error to open a decoder. We prioritize custom decoders so that if they
66254 implement the same encoding format they take priority over the built-in decoders.
66255 */
66256 result = ma_decoder_init_custom_from_memory__internal(pData, dataSize, &config, pDecoder);
66257
66258 /*
66259 If we get to this point and we still haven't found a decoder, and the caller has requested a
66260 specific encoding format, there's no hope for it. Abort.
66261 */
66262 if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) {
66263 return MA_NO_BACKEND;
66264 }
66265
66266 /* Use trial and error for stock decoders. */
66267 if (result != MA_SUCCESS) {
66268 #ifdef MA_HAS_WAV
66269 if (result != MA_SUCCESS) {
66270 result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder);
66271 }
66272 #endif
66273 #ifdef MA_HAS_FLAC
66274 if (result != MA_SUCCESS) {
66275 result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder);
66276 }
66277 #endif
66278 #ifdef MA_HAS_MP3
66279 if (result != MA_SUCCESS) {
66280 result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder);
66281 }
66282 #endif
66283 #ifdef MA_HAS_VORBIS
66284 if (result != MA_SUCCESS) {
66285 result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder);
66286 }
66287 #endif
66288 }
66289 }
66290
66291 /*
66292 If at this point we still haven't successfully initialized the decoder it most likely means
66293 the backend doesn't have an implementation for loading from a file path. We'll try using
66294 miniaudio's built-in file IO for loading file.
66295 */
66296 if (result == MA_SUCCESS) {
66297 /* Initialization was successful. Finish up. */
66298 result = ma_decoder__postinit(&config, pDecoder);
66299 if (result != MA_SUCCESS) {
66300 /*
66301 The backend was initialized successfully, but for some reason post-initialization failed. This is most likely
66302 due to an out of memory error. We're going to abort with an error here and not try to recover.
66303 */
66304 if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {
66305 pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks);
66306 }
66307
66308 return result;
66309 }
66310 } else {
66311 /* Probably no implementation for loading from a block of memory. Use miniaudio's abstraction instead. */
66312 result = ma_decoder__preinit_memory_wrapper(pData, dataSize, &config, pDecoder);
66313 if (result != MA_SUCCESS) {
66314 return result;
66315 }
66316
66317 result = ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder);
66318 if (result != MA_SUCCESS) {
66319 return result;
66320 }
66321 }
66322
66323 return MA_SUCCESS;
66324}
66325
66326
66327#if defined(MA_HAS_WAV) || \
66328 defined(MA_HAS_MP3) || \
66329 defined(MA_HAS_FLAC) || \
66330 defined(MA_HAS_VORBIS)
66331#define MA_HAS_PATH_API
66332#endif
66333
66334#if defined(MA_HAS_PATH_API)
66335static const char* ma_path_file_name(const char* path)
66336{
66337 const char* fileName;
66338
66339 if (path == NULL) {
66340 return NULL;
66341 }
66342
66343 fileName = path;
66344
66345 /* We just loop through the path until we find the last slash. */
66346 while (path[0] != '\0') {
66347 if (path[0] == '/' || path[0] == '\\') {
66348 fileName = path;
66349 }
66350
66351 path += 1;
66352 }
66353
66354 /* At this point the file name is sitting on a slash, so just move forward. */
66355 while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
66356 fileName += 1;
66357 }
66358
66359 return fileName;
66360}
66361
66362static const wchar_t* ma_path_file_name_w(const wchar_t* path)
66363{
66364 const wchar_t* fileName;
66365
66366 if (path == NULL) {
66367 return NULL;
66368 }
66369
66370 fileName = path;
66371
66372 /* We just loop through the path until we find the last slash. */
66373 while (path[0] != '\0') {
66374 if (path[0] == '/' || path[0] == '\\') {
66375 fileName = path;
66376 }
66377
66378 path += 1;
66379 }
66380
66381 /* At this point the file name is sitting on a slash, so just move forward. */
66382 while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
66383 fileName += 1;
66384 }
66385
66386 return fileName;
66387}
66388
66389
66390static const char* ma_path_extension(const char* path)
66391{
66392 const char* extension;
66393 const char* lastOccurance;
66394
66395 if (path == NULL) {
66396 path = "";
66397 }
66398
66399 extension = ma_path_file_name(path);
66400 lastOccurance = NULL;
66401
66402 /* Just find the last '.' and return. */
66403 while (extension[0] != '\0') {
66404 if (extension[0] == '.') {
66405 extension += 1;
66406 lastOccurance = extension;
66407 }
66408
66409 extension += 1;
66410 }
66411
66412 return (lastOccurance != NULL) ? lastOccurance : extension;
66413}
66414
66415static const wchar_t* ma_path_extension_w(const wchar_t* path)
66416{
66417 const wchar_t* extension;
66418 const wchar_t* lastOccurance;
66419
66420 if (path == NULL) {
66421 path = L"";
66422 }
66423
66424 extension = ma_path_file_name_w(path);
66425 lastOccurance = NULL;
66426
66427 /* Just find the last '.' and return. */
66428 while (extension[0] != '\0') {
66429 if (extension[0] == '.') {
66430 extension += 1;
66431 lastOccurance = extension;
66432 }
66433
66434 extension += 1;
66435 }
66436
66437 return (lastOccurance != NULL) ? lastOccurance : extension;
66438}
66439
66440
66441static ma_bool32 ma_path_extension_equal(const char* path, const char* extension)
66442{
66443 const char* ext1;
66444 const char* ext2;
66445
66446 if (path == NULL || extension == NULL) {
66447 return MA_FALSE;
66448 }
66449
66450 ext1 = extension;
66451 ext2 = ma_path_extension(path);
66452
66453#if defined(_MSC_VER) || defined(__DMC__)
66454 return _stricmp(ext1, ext2) == 0;
66455#else
66456 return strcasecmp(ext1, ext2) == 0;
66457#endif
66458}
66459
66460static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension)
66461{
66462 const wchar_t* ext1;
66463 const wchar_t* ext2;
66464
66465 if (path == NULL || extension == NULL) {
66466 return MA_FALSE;
66467 }
66468
66469 ext1 = extension;
66470 ext2 = ma_path_extension_w(path);
66471
66472#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)
66473 return _wcsicmp(ext1, ext2) == 0;
66474#else
66475 /*
66476 I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This
66477 isn't the most efficient way to do it, but it should work OK.
66478 */
66479 {
66480 char ext1MB[4096];
66481 char ext2MB[4096];
66482 const wchar_t* pext1 = ext1;
66483 const wchar_t* pext2 = ext2;
66484 mbstate_t mbs1;
66485 mbstate_t mbs2;
66486
66487 MA_ZERO_OBJECT(&mbs1);
66488 MA_ZERO_OBJECT(&mbs2);
66489
66490 if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) {
66491 return MA_FALSE;
66492 }
66493 if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) {
66494 return MA_FALSE;
66495 }
66496
66497 return strcasecmp(ext1MB, ext2MB) == 0;
66498 }
66499#endif
66500}
66501#endif /* MA_HAS_PATH_API */
66502
66503
66504
66505static ma_result ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
66506{
66507 MA_ASSERT(pDecoder != NULL);
66508 MA_ASSERT(pBufferOut != NULL);
66509
66510 return ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, pBytesRead);
66511}
66512
66513static ma_result ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin)
66514{
66515 MA_ASSERT(pDecoder != NULL);
66516
66517 return ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin);
66518}
66519
66520static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor)
66521{
66522 MA_ASSERT(pDecoder != NULL);
66523
66524 return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor);
66525}
66526
66527static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
66528{
66529 ma_result result;
66530 ma_vfs_file file;
66531
66532 result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder);
66533 if (result != MA_SUCCESS) {
66534 return result;
66535 }
66536
66537 if (pFilePath == NULL || pFilePath[0] == '\0') {
66538 return MA_INVALID_ARGS;
66539 }
66540
66541 result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
66542 if (result != MA_SUCCESS) {
66543 return result;
66544 }
66545
66546 pDecoder->data.vfs.pVFS = pVFS;
66547 pDecoder->data.vfs.file = file;
66548
66549 return MA_SUCCESS;
66550}
66551
66552MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
66553{
66554 ma_result result;
66555 ma_decoder_config config;
66556
66557 config = ma_decoder_config_init_copy(pConfig);
66558 result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder);
66559 if (result != MA_SUCCESS) {
66560 return result;
66561 }
66562
66563 result = MA_NO_BACKEND;
66564
66566 #ifdef MA_HAS_WAV
66567 if (config.encodingFormat == ma_encoding_format_wav) {
66568 result = ma_decoder_init_wav__internal(&config, pDecoder);
66569 }
66570 #endif
66571 #ifdef MA_HAS_FLAC
66573 result = ma_decoder_init_flac__internal(&config, pDecoder);
66574 }
66575 #endif
66576 #ifdef MA_HAS_MP3
66577 if (config.encodingFormat == ma_encoding_format_mp3) {
66578 result = ma_decoder_init_mp3__internal(&config, pDecoder);
66579 }
66580 #endif
66581 #ifdef MA_HAS_VORBIS
66583 result = ma_decoder_init_vorbis__internal(&config, pDecoder);
66584 }
66585 #endif
66586
66587 /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */
66588 if (result != MA_SUCCESS) {
66589 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
66590 }
66591 }
66592
66593 if (result != MA_SUCCESS) {
66594 /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */
66595
66596 /*
66597 We use trial and error to open a decoder. We prioritize custom decoders so that if they
66598 implement the same encoding format they take priority over the built-in decoders.
66599 */
66600 if (result != MA_SUCCESS) {
66601 result = ma_decoder_init_custom__internal(&config, pDecoder);
66602 if (result != MA_SUCCESS) {
66603 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
66604 }
66605 }
66606
66607 /*
66608 If we get to this point and we still haven't found a decoder, and the caller has requested a
66609 specific encoding format, there's no hope for it. Abort.
66610 */
66612 return MA_NO_BACKEND;
66613 }
66614
66615 #ifdef MA_HAS_WAV
66616 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) {
66617 result = ma_decoder_init_wav__internal(&config, pDecoder);
66618 if (result != MA_SUCCESS) {
66619 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
66620 }
66621 }
66622 #endif
66623 #ifdef MA_HAS_FLAC
66624 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) {
66625 result = ma_decoder_init_flac__internal(&config, pDecoder);
66626 if (result != MA_SUCCESS) {
66627 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
66628 }
66629 }
66630 #endif
66631 #ifdef MA_HAS_MP3
66632 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) {
66633 result = ma_decoder_init_mp3__internal(&config, pDecoder);
66634 if (result != MA_SUCCESS) {
66635 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
66636 }
66637 }
66638 #endif
66639 }
66640
66641 /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */
66642 if (result != MA_SUCCESS) {
66643 result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder);
66644 } else {
66645 result = ma_decoder__postinit(&config, pDecoder);
66646 }
66647
66648 if (result != MA_SUCCESS) {
66649 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. */
66650 ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
66651 }
66652
66653 return result;
66654 }
66655
66656 return MA_SUCCESS;
66657}
66658
66659
66660static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
66661{
66662 ma_result result;
66663 ma_vfs_file file;
66664
66665 result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder);
66666 if (result != MA_SUCCESS) {
66667 return result;
66668 }
66669
66670 if (pFilePath == NULL || pFilePath[0] == '\0') {
66671 return MA_INVALID_ARGS;
66672 }
66673
66674 result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
66675 if (result != MA_SUCCESS) {
66676 return result;
66677 }
66678
66679 pDecoder->data.vfs.pVFS = pVFS;
66680 pDecoder->data.vfs.file = file;
66681
66682 return MA_SUCCESS;
66683}
66684
66685MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
66686{
66687 ma_result result;
66688 ma_decoder_config config;
66689
66690 config = ma_decoder_config_init_copy(pConfig);
66691 result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder);
66692 if (result != MA_SUCCESS) {
66693 return result;
66694 }
66695
66696 result = MA_NO_BACKEND;
66697
66699 #ifdef MA_HAS_WAV
66700 if (config.encodingFormat == ma_encoding_format_wav) {
66701 result = ma_decoder_init_wav__internal(&config, pDecoder);
66702 }
66703 #endif
66704 #ifdef MA_HAS_FLAC
66706 result = ma_decoder_init_flac__internal(&config, pDecoder);
66707 }
66708 #endif
66709 #ifdef MA_HAS_MP3
66710 if (config.encodingFormat == ma_encoding_format_mp3) {
66711 result = ma_decoder_init_mp3__internal(&config, pDecoder);
66712 }
66713 #endif
66714 #ifdef MA_HAS_VORBIS
66716 result = ma_decoder_init_vorbis__internal(&config, pDecoder);
66717 }
66718 #endif
66719
66720 /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */
66721 if (result != MA_SUCCESS) {
66722 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
66723 }
66724 }
66725
66726 if (result != MA_SUCCESS) {
66727 /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */
66728
66729 /*
66730 We use trial and error to open a decoder. We prioritize custom decoders so that if they
66731 implement the same encoding format they take priority over the built-in decoders.
66732 */
66733 if (result != MA_SUCCESS) {
66734 result = ma_decoder_init_custom__internal(&config, pDecoder);
66735 if (result != MA_SUCCESS) {
66736 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
66737 }
66738 }
66739
66740 /*
66741 If we get to this point and we still haven't found a decoder, and the caller has requested a
66742 specific encoding format, there's no hope for it. Abort.
66743 */
66745 return MA_NO_BACKEND;
66746 }
66747
66748 #ifdef MA_HAS_WAV
66749 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) {
66750 result = ma_decoder_init_wav__internal(&config, pDecoder);
66751 if (result != MA_SUCCESS) {
66752 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
66753 }
66754 }
66755 #endif
66756 #ifdef MA_HAS_FLAC
66757 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) {
66758 result = ma_decoder_init_flac__internal(&config, pDecoder);
66759 if (result != MA_SUCCESS) {
66760 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
66761 }
66762 }
66763 #endif
66764 #ifdef MA_HAS_MP3
66765 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) {
66766 result = ma_decoder_init_mp3__internal(&config, pDecoder);
66767 if (result != MA_SUCCESS) {
66768 ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
66769 }
66770 }
66771 #endif
66772 }
66773
66774 /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */
66775 if (result != MA_SUCCESS) {
66776 result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder);
66777 } else {
66778 result = ma_decoder__postinit(&config, pDecoder);
66779 }
66780
66781 if (result != MA_SUCCESS) {
66782 ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
66783 return result;
66784 }
66785
66786 return MA_SUCCESS;
66787}
66788
66789
66790static ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
66791{
66792 ma_result result;
66793
66794 result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder);
66795 if (result != MA_SUCCESS) {
66796 return result;
66797 }
66798
66799 if (pFilePath == NULL || pFilePath[0] == '\0') {
66800 return MA_INVALID_ARGS;
66801 }
66802
66803 return MA_SUCCESS;
66804}
66805
66806MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
66807{
66808 ma_result result;
66809 ma_decoder_config config;
66810
66811 config = ma_decoder_config_init_copy(pConfig);
66812 result = ma_decoder__preinit_file(pFilePath, &config, pDecoder);
66813 if (result != MA_SUCCESS) {
66814 return result;
66815 }
66816
66817 /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */
66818 result = MA_NO_BACKEND;
66819
66821 #ifdef MA_HAS_WAV
66822 if (config.encodingFormat == ma_encoding_format_wav) {
66823 result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder);
66824 }
66825 #endif
66826 #ifdef MA_HAS_FLAC
66828 result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder);
66829 }
66830 #endif
66831 #ifdef MA_HAS_MP3
66832 if (config.encodingFormat == ma_encoding_format_mp3) {
66833 result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder);
66834 }
66835 #endif
66836 #ifdef MA_HAS_VORBIS
66838 result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder);
66839 }
66840 #endif
66841 }
66842
66843 if (result != MA_SUCCESS) {
66844 /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */
66845
66846 /*
66847 We use trial and error to open a decoder. We prioritize custom decoders so that if they
66848 implement the same encoding format they take priority over the built-in decoders.
66849 */
66850 result = ma_decoder_init_custom_from_file__internal(pFilePath, &config, pDecoder);
66851
66852 /*
66853 If we get to this point and we still haven't found a decoder, and the caller has requested a
66854 specific encoding format, there's no hope for it. Abort.
66855 */
66856 if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) {
66857 return MA_NO_BACKEND;
66858 }
66859
66860 /* First try loading based on the file extension so we don't waste time opening and closing files. */
66861 #ifdef MA_HAS_WAV
66862 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) {
66863 result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder);
66864 }
66865 #endif
66866 #ifdef MA_HAS_FLAC
66867 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) {
66868 result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder);
66869 }
66870 #endif
66871 #ifdef MA_HAS_MP3
66872 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) {
66873 result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder);
66874 }
66875 #endif
66876 #ifdef MA_HAS_VORBIS
66877 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "ogg")) {
66878 result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder);
66879 }
66880 #endif
66881
66882 /*
66883 If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we
66884 need only iterate over our stock decoders.
66885 */
66886 if (result != MA_SUCCESS) {
66887 #ifdef MA_HAS_WAV
66888 if (result != MA_SUCCESS) {
66889 result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder);
66890 }
66891 #endif
66892 #ifdef MA_HAS_FLAC
66893 if (result != MA_SUCCESS) {
66894 result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder);
66895 }
66896 #endif
66897 #ifdef MA_HAS_MP3
66898 if (result != MA_SUCCESS) {
66899 result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder);
66900 }
66901 #endif
66902 #ifdef MA_HAS_VORBIS
66903 if (result != MA_SUCCESS) {
66904 result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder);
66905 }
66906 #endif
66907 }
66908 }
66909
66910 /*
66911 If at this point we still haven't successfully initialized the decoder it most likely means
66912 the backend doesn't have an implementation for loading from a file path. We'll try using
66913 miniaudio's built-in file IO for loading file.
66914 */
66915 if (result == MA_SUCCESS) {
66916 /* Initialization was successful. Finish up. */
66917 result = ma_decoder__postinit(&config, pDecoder);
66918 if (result != MA_SUCCESS) {
66919 /*
66920 The backend was initialized successfully, but for some reason post-initialization failed. This is most likely
66921 due to an out of memory error. We're going to abort with an error here and not try to recover.
66922 */
66923 if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {
66924 pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks);
66925 }
66926
66927 return result;
66928 }
66929 } else {
66930 /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */
66931 result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder);
66932 if (result != MA_SUCCESS) {
66933 return result;
66934 }
66935 }
66936
66937 return MA_SUCCESS;
66938}
66939
66940static ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
66941{
66942 ma_result result;
66943
66944 result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder);
66945 if (result != MA_SUCCESS) {
66946 return result;
66947 }
66948
66949 if (pFilePath == NULL || pFilePath[0] == '\0') {
66950 return MA_INVALID_ARGS;
66951 }
66952
66953 return MA_SUCCESS;
66954}
66955
66956MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
66957{
66958 ma_result result;
66959 ma_decoder_config config;
66960
66961 config = ma_decoder_config_init_copy(pConfig);
66962 result = ma_decoder__preinit_file_w(pFilePath, &config, pDecoder);
66963 if (result != MA_SUCCESS) {
66964 return result;
66965 }
66966
66967 /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */
66968 result = MA_NO_BACKEND;
66969
66971 #ifdef MA_HAS_WAV
66972 if (config.encodingFormat == ma_encoding_format_wav) {
66973 result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder);
66974 }
66975 #endif
66976 #ifdef MA_HAS_FLAC
66978 result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder);
66979 }
66980 #endif
66981 #ifdef MA_HAS_MP3
66982 if (config.encodingFormat == ma_encoding_format_mp3) {
66983 result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder);
66984 }
66985 #endif
66986 #ifdef MA_HAS_VORBIS
66988 result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder);
66989 }
66990 #endif
66991 }
66992
66993 if (result != MA_SUCCESS) {
66994 /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */
66995
66996 /*
66997 We use trial and error to open a decoder. We prioritize custom decoders so that if they
66998 implement the same encoding format they take priority over the built-in decoders.
66999 */
67000 result = ma_decoder_init_custom_from_file_w__internal(pFilePath, &config, pDecoder);
67001
67002 /*
67003 If we get to this point and we still haven't found a decoder, and the caller has requested a
67004 specific encoding format, there's no hope for it. Abort.
67005 */
67006 if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) {
67007 return MA_NO_BACKEND;
67008 }
67009
67010 /* First try loading based on the file extension so we don't waste time opening and closing files. */
67011 #ifdef MA_HAS_WAV
67012 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) {
67013 result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder);
67014 }
67015 #endif
67016 #ifdef MA_HAS_FLAC
67017 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) {
67018 result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder);
67019 }
67020 #endif
67021 #ifdef MA_HAS_MP3
67022 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) {
67023 result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder);
67024 }
67025 #endif
67026 #ifdef MA_HAS_VORBIS
67027 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"ogg")) {
67028 result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder);
67029 }
67030 #endif
67031
67032 /*
67033 If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we
67034 need only iterate over our stock decoders.
67035 */
67036 if (result != MA_SUCCESS) {
67037 #ifdef MA_HAS_WAV
67038 if (result != MA_SUCCESS) {
67039 result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder);
67040 }
67041 #endif
67042 #ifdef MA_HAS_FLAC
67043 if (result != MA_SUCCESS) {
67044 result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder);
67045 }
67046 #endif
67047 #ifdef MA_HAS_MP3
67048 if (result != MA_SUCCESS) {
67049 result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder);
67050 }
67051 #endif
67052 #ifdef MA_HAS_VORBIS
67053 if (result != MA_SUCCESS) {
67054 result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder);
67055 }
67056 #endif
67057 }
67058 }
67059
67060 /*
67061 If at this point we still haven't successfully initialized the decoder it most likely means
67062 the backend doesn't have an implementation for loading from a file path. We'll try using
67063 miniaudio's built-in file IO for loading file.
67064 */
67065 if (result == MA_SUCCESS) {
67066 /* Initialization was successful. Finish up. */
67067 result = ma_decoder__postinit(&config, pDecoder);
67068 if (result != MA_SUCCESS) {
67069 /*
67070 The backend was initialized successfully, but for some reason post-initialization failed. This is most likely
67071 due to an out of memory error. We're going to abort with an error here and not try to recover.
67072 */
67073 if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {
67074 pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks);
67075 }
67076
67077 return result;
67078 }
67079 } else {
67080 /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */
67081 result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder);
67082 if (result != MA_SUCCESS) {
67083 return result;
67084 }
67085 }
67086
67087 return MA_SUCCESS;
67088}
67089
67091{
67092 if (pDecoder == NULL) {
67093 return MA_INVALID_ARGS;
67094 }
67095
67096 if (pDecoder->pBackend != NULL) {
67097 if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {
67098 pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks);
67099 }
67100 }
67101
67102 if (pDecoder->onRead == ma_decoder__on_read_vfs) {
67103 ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file);
67104 pDecoder->data.vfs.file = NULL;
67105 }
67106
67108 ma_data_source_uninit(&pDecoder->ds);
67109
67110 if (pDecoder->pInputCache != NULL) {
67111 ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks);
67112 }
67113
67114 return MA_SUCCESS;
67115}
67116
67117MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
67118{
67119 ma_result result = MA_SUCCESS;
67120 ma_uint64 totalFramesReadOut;
67121 void* pRunningFramesOut;
67122
67123 if (pFramesRead != NULL) {
67124 *pFramesRead = 0; /* Safety. */
67125 }
67126
67127 if (frameCount == 0) {
67128 return MA_INVALID_ARGS;
67129 }
67130
67131 if (pDecoder == NULL) {
67132 return MA_INVALID_ARGS;
67133 }
67134
67135 if (pDecoder->pBackend == NULL) {
67136 return MA_INVALID_OPERATION;
67137 }
67138
67139 /* Fast path. */
67140 if (pDecoder->converter.isPassthrough) {
67141 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut);
67142 } else {
67143 /*
67144 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
67145 need to run through each sample because we need to ensure its internal cache is updated.
67146 */
67147 if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) {
67148 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut);
67149 } else {
67150 /* Slow path. Need to run everything through the data converter. */
67151 ma_format internalFormat;
67152 ma_uint32 internalChannels;
67153
67154 totalFramesReadOut = 0;
67155 pRunningFramesOut = pFramesOut;
67156
67157 result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0);
67158 if (result != MA_SUCCESS) {
67159 return result; /* Failed to retrieve the internal format and channel count. */
67160 }
67161
67162 /*
67163 We run a different path depending on whether or not we are using a heap-allocated
67164 intermediary buffer or not. If the data converter does not support the calculation of
67165 the required number of input frames, we'll use the heap-allocated path. Otherwise we'll
67166 use the stack-allocated path.
67167 */
67168 if (pDecoder->pInputCache != NULL) {
67169 /* We don't have a way of determining the required number of input frames, so need to persistently store input data in a cache. */
67170 while (totalFramesReadOut < frameCount) {
67171 ma_uint64 framesToReadThisIterationIn;
67172 ma_uint64 framesToReadThisIterationOut;
67173
67174 /* If there's any data available in the cache, that needs to get processed first. */
67175 if (pDecoder->inputCacheRemaining > 0) {
67176 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
67177 framesToReadThisIterationIn = framesToReadThisIterationOut;
67178 if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) {
67179 framesToReadThisIterationIn = pDecoder->inputCacheRemaining;
67180 }
67181
67182 result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut);
67183 if (result != MA_SUCCESS) {
67184 break;
67185 }
67186
67187 pDecoder->inputCacheConsumed += framesToReadThisIterationIn;
67188 pDecoder->inputCacheRemaining -= framesToReadThisIterationIn;
67189
67190 totalFramesReadOut += framesToReadThisIterationOut;
67191
67192 if (pRunningFramesOut != NULL) {
67193 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
67194 }
67195
67196 if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) {
67197 break; /* We're done. */
67198 }
67199 }
67200
67201 /* Getting here means there's no data in the cache and we need to fill it up from the data source. */
67202 if (pDecoder->inputCacheRemaining == 0) {
67203 pDecoder->inputCacheConsumed = 0;
67204
67205 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining);
67206 if (result != MA_SUCCESS) {
67207 break;
67208 }
67209 }
67210 }
67211 } else {
67212 /* We have a way of determining the required number of input frames so just use the stack. */
67213 while (totalFramesReadOut < frameCount) {
67214 ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */
67215 ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels);
67216 ma_uint64 framesToReadThisIterationIn;
67217 ma_uint64 framesReadThisIterationIn;
67218 ma_uint64 framesToReadThisIterationOut;
67219 ma_uint64 framesReadThisIterationOut;
67220 ma_uint64 requiredInputFrameCount;
67221
67222 framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
67223 framesToReadThisIterationIn = framesToReadThisIterationOut;
67224 if (framesToReadThisIterationIn > intermediaryBufferCap) {
67225 framesToReadThisIterationIn = intermediaryBufferCap;
67226 }
67227
67228 ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount);
67229 if (framesToReadThisIterationIn > requiredInputFrameCount) {
67230 framesToReadThisIterationIn = requiredInputFrameCount;
67231 }
67232
67233 if (requiredInputFrameCount > 0) {
67234 result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn);
67235
67236 /*
67237 Note here that even if we've reached the end, we don't want to abort because there might be more output frames needing to be
67238 generated from cached input data, which might happen if resampling is being performed.
67239 */
67240 if (result != MA_SUCCESS && result != MA_AT_END) {
67241 break;
67242 }
67243 } else {
67244 framesReadThisIterationIn = 0;
67245 pIntermediaryBuffer[0] = 0; /* <-- This is just to silence a static analysis warning. */
67246 }
67247
67248 /*
67249 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
67250 input frames, we still want to try processing frames because there may some output frames generated from cached input data.
67251 */
67252 framesReadThisIterationOut = framesToReadThisIterationOut;
67253 result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
67254 if (result != MA_SUCCESS) {
67255 break;
67256 }
67257
67258 totalFramesReadOut += framesReadThisIterationOut;
67259
67260 if (pRunningFramesOut != NULL) {
67261 pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
67262 }
67263
67264 if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
67265 break; /* We're done. */
67266 }
67267 }
67268 }
67269 }
67270 }
67271
67272 pDecoder->readPointerInPCMFrames += totalFramesReadOut;
67273
67274 if (pFramesRead != NULL) {
67275 *pFramesRead = totalFramesReadOut;
67276 }
67277
67278 if (result == MA_SUCCESS && totalFramesReadOut == 0) {
67279 result = MA_AT_END;
67280 }
67281
67282 return result;
67283}
67284
67286{
67287 if (pDecoder == NULL) {
67288 return MA_INVALID_ARGS;
67289 }
67290
67291 if (pDecoder->pBackend != NULL) {
67292 ma_result result;
67293 ma_uint64 internalFrameIndex;
67294 ma_uint32 internalSampleRate;
67295 ma_uint64 currentFrameIndex;
67296
67297 result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0);
67298 if (result != MA_SUCCESS) {
67299 return result; /* Failed to retrieve the internal sample rate. */
67300 }
67301
67302 if (internalSampleRate == pDecoder->outputSampleRate) {
67303 internalFrameIndex = frameIndex;
67304 } else {
67305 internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex);
67306 }
67307
67308 /* Only seek if we're requesting a different frame to what we're currently sitting on. */
67309 ma_data_source_get_cursor_in_pcm_frames(pDecoder->pBackend, &currentFrameIndex);
67310 if (currentFrameIndex != internalFrameIndex) {
67311 result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex);
67312 if (result == MA_SUCCESS) {
67313 pDecoder->readPointerInPCMFrames = frameIndex;
67314 }
67315
67316 /* Reset the data converter so that any cached data in the resampler is cleared. */
67318 }
67319
67320 return result;
67321 }
67322
67323 /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */
67324 return MA_INVALID_ARGS;
67325}
67326
67327MA_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)
67328{
67329 if (pDecoder == NULL) {
67330 return MA_INVALID_ARGS;
67331 }
67332
67333 if (pFormat != NULL) {
67334 *pFormat = pDecoder->outputFormat;
67335 }
67336
67337 if (pChannels != NULL) {
67338 *pChannels = pDecoder->outputChannels;
67339 }
67340
67341 if (pSampleRate != NULL) {
67342 *pSampleRate = pDecoder->outputSampleRate;
67343 }
67344
67345 if (pChannelMap != NULL) {
67346 ma_data_converter_get_output_channel_map(&pDecoder->converter, pChannelMap, channelMapCap);
67347 }
67348
67349 return MA_SUCCESS;
67350}
67351
67353{
67354 if (pCursor == NULL) {
67355 return MA_INVALID_ARGS;
67356 }
67357
67358 *pCursor = 0;
67359
67360 if (pDecoder == NULL) {
67361 return MA_INVALID_ARGS;
67362 }
67363
67364 *pCursor = pDecoder->readPointerInPCMFrames;
67365
67366 return MA_SUCCESS;
67367}
67368
67370{
67371 if (pLength == NULL) {
67372 return MA_INVALID_ARGS;
67373 }
67374
67375 *pLength = 0;
67376
67377 if (pDecoder == NULL) {
67378 return MA_INVALID_ARGS;
67379 }
67380
67381 if (pDecoder->pBackend != NULL) {
67382 ma_result result;
67383 ma_uint64 internalLengthInPCMFrames;
67384 ma_uint32 internalSampleRate;
67385
67386 result = ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &internalLengthInPCMFrames);
67387 if (result != MA_SUCCESS) {
67388 return result; /* Failed to retrieve the internal length. */
67389 }
67390
67391 result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0);
67392 if (result != MA_SUCCESS) {
67393 return result; /* Failed to retrieve the internal sample rate. */
67394 }
67395
67396 if (internalSampleRate == pDecoder->outputSampleRate) {
67397 *pLength = internalLengthInPCMFrames;
67398 } else {
67399 *pLength = ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, internalLengthInPCMFrames);
67400 }
67401
67402 return MA_SUCCESS;
67403 } else {
67404 return MA_NO_BACKEND;
67405 }
67406}
67407
67409{
67410 ma_result result;
67411 ma_uint64 totalFrameCount;
67412
67413 if (pAvailableFrames == NULL) {
67414 return MA_INVALID_ARGS;
67415 }
67416
67417 *pAvailableFrames = 0;
67418
67419 if (pDecoder == NULL) {
67420 return MA_INVALID_ARGS;
67421 }
67422
67423 result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount);
67424 if (result != MA_SUCCESS) {
67425 return result;
67426 }
67427
67428 if (totalFrameCount <= pDecoder->readPointerInPCMFrames) {
67429 *pAvailableFrames = 0;
67430 } else {
67431 *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames;
67432 }
67433
67434 return MA_SUCCESS;
67435}
67436
67437
67438static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
67439{
67440 ma_result result;
67441 ma_uint64 totalFrameCount;
67442 ma_uint64 bpf;
67443 ma_uint64 dataCapInFrames;
67444 void* pPCMFramesOut;
67445
67446 MA_ASSERT(pDecoder != NULL);
67447
67448 totalFrameCount = 0;
67449 bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
67450
67451 /* The frame count is unknown until we try reading. Thus, we just run in a loop. */
67452 dataCapInFrames = 0;
67453 pPCMFramesOut = NULL;
67454 for (;;) {
67455 ma_uint64 frameCountToTryReading;
67456 ma_uint64 framesJustRead;
67457
67458 /* Make room if there's not enough. */
67459 if (totalFrameCount == dataCapInFrames) {
67460 void* pNewPCMFramesOut;
67461 ma_uint64 newDataCapInFrames = dataCapInFrames*2;
67462 if (newDataCapInFrames == 0) {
67463 newDataCapInFrames = 4096;
67464 }
67465
67466 if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) {
67467 ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);
67468 return MA_TOO_BIG;
67469 }
67470
67471 pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), &pDecoder->allocationCallbacks);
67472 if (pNewPCMFramesOut == NULL) {
67473 ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);
67474 return MA_OUT_OF_MEMORY;
67475 }
67476
67477 dataCapInFrames = newDataCapInFrames;
67478 pPCMFramesOut = pNewPCMFramesOut;
67479 }
67480
67481 frameCountToTryReading = dataCapInFrames - totalFrameCount;
67482 MA_ASSERT(frameCountToTryReading > 0);
67483
67484 result = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading, &framesJustRead);
67485 totalFrameCount += framesJustRead;
67486
67487 if (result != MA_SUCCESS) {
67488 break;
67489 }
67490
67491 if (framesJustRead < frameCountToTryReading) {
67492 break;
67493 }
67494 }
67495
67496
67497 if (pConfigOut != NULL) {
67498 pConfigOut->format = pDecoder->outputFormat;
67499 pConfigOut->channels = pDecoder->outputChannels;
67500 pConfigOut->sampleRate = pDecoder->outputSampleRate;
67501 }
67502
67503 if (ppPCMFramesOut != NULL) {
67504 *ppPCMFramesOut = pPCMFramesOut;
67505 } else {
67506 ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);
67507 }
67508
67509 if (pFrameCountOut != NULL) {
67510 *pFrameCountOut = totalFrameCount;
67511 }
67512
67513 ma_decoder_uninit(pDecoder);
67514 return MA_SUCCESS;
67515}
67516
67517MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
67518{
67519 ma_result result;
67520 ma_decoder_config config;
67521 ma_decoder decoder;
67522
67523 if (pFrameCountOut != NULL) {
67524 *pFrameCountOut = 0;
67525 }
67526 if (ppPCMFramesOut != NULL) {
67527 *ppPCMFramesOut = NULL;
67528 }
67529
67530 config = ma_decoder_config_init_copy(pConfig);
67531
67532 result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder);
67533 if (result != MA_SUCCESS) {
67534 return result;
67535 }
67536
67537 result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
67538
67539 return result;
67540}
67541
67542MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
67543{
67544 return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut);
67545}
67546
67547MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
67548{
67549 ma_decoder_config config;
67550 ma_decoder decoder;
67551 ma_result result;
67552
67553 if (pFrameCountOut != NULL) {
67554 *pFrameCountOut = 0;
67555 }
67556 if (ppPCMFramesOut != NULL) {
67557 *ppPCMFramesOut = NULL;
67558 }
67559
67560 if (pData == NULL || dataSize == 0) {
67561 return MA_INVALID_ARGS;
67562 }
67563
67564 config = ma_decoder_config_init_copy(pConfig);
67565
67566 result = ma_decoder_init_memory(pData, dataSize, &config, &decoder);
67567 if (result != MA_SUCCESS) {
67568 return result;
67569 }
67570
67571 return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
67572}
67573#endif /* MA_NO_DECODING */
67574
67575
67576#ifndef MA_NO_ENCODING
67577
67578#if defined(MA_HAS_WAV)
67579static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite)
67580{
67581 ma_encoder* pEncoder = (ma_encoder*)pUserData;
67582 size_t bytesWritten = 0;
67583
67584 MA_ASSERT(pEncoder != NULL);
67585
67586 pEncoder->onWrite(pEncoder, pData, bytesToWrite, &bytesWritten);
67587 return bytesWritten;
67588}
67589
67590static ma_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, ma_dr_wav_seek_origin origin)
67591{
67592 ma_encoder* pEncoder = (ma_encoder*)pUserData;
67593 ma_result result;
67594 ma_seek_origin maSeekOrigin;
67595
67596 MA_ASSERT(pEncoder != NULL);
67597
67598 maSeekOrigin = ma_seek_origin_start;
67599 if (origin == MA_DR_WAV_SEEK_CUR) {
67600 maSeekOrigin = ma_seek_origin_current;
67601 } else if (origin == MA_DR_WAV_SEEK_END) {
67602 maSeekOrigin = ma_seek_origin_end;
67603 }
67604
67605 result = pEncoder->onSeek(pEncoder, offset, maSeekOrigin);
67606 if (result != MA_SUCCESS) {
67607 return MA_FALSE;
67608 } else {
67609 return MA_TRUE;
67610 }
67611}
67612
67613static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder)
67614{
67615 ma_dr_wav_data_format wavFormat;
67616 ma_allocation_callbacks allocationCallbacks;
67617 ma_dr_wav* pWav;
67618
67619 MA_ASSERT(pEncoder != NULL);
67620
67621 pWav = (ma_dr_wav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks);
67622 if (pWav == NULL) {
67623 return MA_OUT_OF_MEMORY;
67624 }
67625
67626 wavFormat.container = ma_dr_wav_container_riff;
67627 wavFormat.channels = pEncoder->config.channels;
67628 wavFormat.sampleRate = pEncoder->config.sampleRate;
67629 wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8;
67630 if (pEncoder->config.format == ma_format_f32) {
67631 wavFormat.format = MA_DR_WAVE_FORMAT_IEEE_FLOAT;
67632 } else {
67633 wavFormat.format = MA_DR_WAVE_FORMAT_PCM;
67634 }
67635
67636 allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData;
67637 allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc;
67638 allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc;
67639 allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree;
67640
67641 if (!ma_dr_wav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) {
67642 return MA_ERROR;
67643 }
67644
67645 pEncoder->pInternalEncoder = pWav;
67646
67647 return MA_SUCCESS;
67648}
67649
67650static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder)
67651{
67652 ma_dr_wav* pWav;
67653
67654 MA_ASSERT(pEncoder != NULL);
67655
67656 pWav = (ma_dr_wav*)pEncoder->pInternalEncoder;
67657 MA_ASSERT(pWav != NULL);
67658
67659 ma_dr_wav_uninit(pWav);
67660 ma_free(pWav, &pEncoder->config.allocationCallbacks);
67661}
67662
67663static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten)
67664{
67665 ma_dr_wav* pWav;
67666 ma_uint64 framesWritten;
67667
67668 MA_ASSERT(pEncoder != NULL);
67669
67670 pWav = (ma_dr_wav*)pEncoder->pInternalEncoder;
67671 MA_ASSERT(pWav != NULL);
67672
67673 framesWritten = ma_dr_wav_write_pcm_frames(pWav, frameCount, pFramesIn);
67674
67675 if (pFramesWritten != NULL) {
67676 *pFramesWritten = framesWritten;
67677 }
67678
67679 return MA_SUCCESS;
67680}
67681#endif
67682
67683MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
67684{
67685 ma_encoder_config config;
67686
67687 MA_ZERO_OBJECT(&config);
67688 config.encodingFormat = encodingFormat;
67689 config.format = format;
67690 config.channels = channels;
67691 config.sampleRate = sampleRate;
67692
67693 return config;
67694}
67695
67696MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder)
67697{
67698 ma_result result;
67699
67700 if (pEncoder == NULL) {
67701 return MA_INVALID_ARGS;
67702 }
67703
67704 MA_ZERO_OBJECT(pEncoder);
67705
67706 if (pConfig == NULL) {
67707 return MA_INVALID_ARGS;
67708 }
67709
67710 if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) {
67711 return MA_INVALID_ARGS;
67712 }
67713
67714 pEncoder->config = *pConfig;
67715
67716 result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks);
67717 if (result != MA_SUCCESS) {
67718 return result;
67719 }
67720
67721 return MA_SUCCESS;
67722}
67723
67724MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder)
67725{
67726 ma_result result = MA_SUCCESS;
67727
67728 /* This assumes ma_encoder_preinit() has been called prior. */
67729 MA_ASSERT(pEncoder != NULL);
67730
67731 if (onWrite == NULL || onSeek == NULL) {
67732 return MA_INVALID_ARGS;
67733 }
67734
67735 pEncoder->onWrite = onWrite;
67736 pEncoder->onSeek = onSeek;
67737 pEncoder->pUserData = pUserData;
67738
67739 switch (pEncoder->config.encodingFormat)
67740 {
67742 {
67743 #if defined(MA_HAS_WAV)
67744 pEncoder->onInit = ma_encoder__on_init_wav;
67745 pEncoder->onUninit = ma_encoder__on_uninit_wav;
67746 pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav;
67747 #else
67748 result = MA_NO_BACKEND;
67749 #endif
67750 } break;
67751
67752 default:
67753 {
67754 result = MA_INVALID_ARGS;
67755 } break;
67756 }
67757
67758 /* Getting here means we should have our backend callbacks set up. */
67759 if (result == MA_SUCCESS) {
67760 result = pEncoder->onInit(pEncoder);
67761 }
67762
67763 return result;
67764}
67765
67766static ma_result ma_encoder__on_write_vfs(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten)
67767{
67768 return ma_vfs_or_default_write(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, pBufferIn, bytesToWrite, pBytesWritten);
67769}
67770
67771static ma_result ma_encoder__on_seek_vfs(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin)
67772{
67773 return ma_vfs_or_default_seek(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, offset, origin);
67774}
67775
67776MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
67777{
67778 ma_result result;
67779 ma_vfs_file file;
67780
67781 result = ma_encoder_preinit(pConfig, pEncoder);
67782 if (result != MA_SUCCESS) {
67783 return result;
67784 }
67785
67786 /* Now open the file. If this fails we don't need to uninitialize the encoder. */
67787 result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file);
67788 if (result != MA_SUCCESS) {
67789 return result;
67790 }
67791
67792 pEncoder->data.vfs.pVFS = pVFS;
67793 pEncoder->data.vfs.file = file;
67794
67795 result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder);
67796 if (result != MA_SUCCESS) {
67797 ma_vfs_or_default_close(pVFS, file);
67798 return result;
67799 }
67800
67801 return MA_SUCCESS;
67802}
67803
67804MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
67805{
67806 ma_result result;
67807 ma_vfs_file file;
67808
67809 result = ma_encoder_preinit(pConfig, pEncoder);
67810 if (result != MA_SUCCESS) {
67811 return result;
67812 }
67813
67814 /* Now open the file. If this fails we don't need to uninitialize the encoder. */
67815 result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file);
67816 if (result != MA_SUCCESS) {
67817 return result;
67818 }
67819
67820 pEncoder->data.vfs.pVFS = pVFS;
67821 pEncoder->data.vfs.file = file;
67822
67823 result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder);
67824 if (result != MA_SUCCESS) {
67825 ma_vfs_or_default_close(pVFS, file);
67826 return result;
67827 }
67828
67829 return MA_SUCCESS;
67830}
67831
67832MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
67833{
67834 return ma_encoder_init_vfs(NULL, pFilePath, pConfig, pEncoder);
67835}
67836
67837MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
67838{
67839 return ma_encoder_init_vfs_w(NULL, pFilePath, pConfig, pEncoder);
67840}
67841
67842MA_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)
67843{
67844 ma_result result;
67845
67846 result = ma_encoder_preinit(pConfig, pEncoder);
67847 if (result != MA_SUCCESS) {
67848 return result;
67849 }
67850
67851 return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder);
67852}
67853
67854
67855MA_API void ma_encoder_uninit(ma_encoder* pEncoder)
67856{
67857 if (pEncoder == NULL) {
67858 return;
67859 }
67860
67861 if (pEncoder->onUninit) {
67862 pEncoder->onUninit(pEncoder);
67863 }
67864
67865 /* If we have a file handle, close it. */
67866 if (pEncoder->onWrite == ma_encoder__on_write_vfs) {
67867 ma_vfs_or_default_close(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file);
67868 pEncoder->data.vfs.file = NULL;
67869 }
67870}
67871
67872
67873MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten)
67874{
67875 if (pFramesWritten != NULL) {
67876 *pFramesWritten = 0;
67877 }
67878
67879 if (pEncoder == NULL || pFramesIn == NULL) {
67880 return MA_INVALID_ARGS;
67881 }
67882
67883 return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount, pFramesWritten);
67884}
67885#endif /* MA_NO_ENCODING */
67886
67887
67888
67889/**************************************************************************************************************************************************************
67890
67891Generation
67892
67893**************************************************************************************************************************************************************/
67894#ifndef MA_NO_GENERATION
67895MA_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)
67896{
67897 ma_waveform_config config;
67898
67899 MA_ZERO_OBJECT(&config);
67900 config.format = format;
67901 config.channels = channels;
67902 config.sampleRate = sampleRate;
67903 config.type = type;
67904 config.amplitude = amplitude;
67905 config.frequency = frequency;
67906
67907 return config;
67908}
67909
67910static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
67911{
67912 return ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount, pFramesRead);
67913}
67914
67915static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
67916{
67917 return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex);
67918}
67919
67920static 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)
67921{
67922 ma_waveform* pWaveform = (ma_waveform*)pDataSource;
67923
67924 *pFormat = pWaveform->config.format;
67925 *pChannels = pWaveform->config.channels;
67926 *pSampleRate = pWaveform->config.sampleRate;
67927 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pWaveform->config.channels);
67928
67929 return MA_SUCCESS;
67930}
67931
67932static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
67933{
67934 ma_waveform* pWaveform = (ma_waveform*)pDataSource;
67935
67936 *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance);
67937
67938 return MA_SUCCESS;
67939}
67940
67941static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency)
67942{
67943 return (1.0 / (sampleRate / frequency));
67944}
67945
67946static void ma_waveform__update_advance(ma_waveform* pWaveform)
67947{
67948 pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency);
67949}
67950
67951static ma_data_source_vtable g_ma_waveform_data_source_vtable =
67952{
67953 ma_waveform__data_source_on_read,
67954 ma_waveform__data_source_on_seek,
67955 ma_waveform__data_source_on_get_data_format,
67956 ma_waveform__data_source_on_get_cursor,
67957 NULL, /* onGetLength. There's no notion of a length in waveforms. */
67958 NULL, /* onSetLooping */
67959 0
67960};
67961
67962MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform)
67963{
67964 ma_result result;
67965 ma_data_source_config dataSourceConfig;
67966
67967 if (pWaveform == NULL) {
67968 return MA_INVALID_ARGS;
67969 }
67970
67971 MA_ZERO_OBJECT(pWaveform);
67972
67973 dataSourceConfig = ma_data_source_config_init();
67974 dataSourceConfig.vtable = &g_ma_waveform_data_source_vtable;
67975
67976 result = ma_data_source_init(&dataSourceConfig, &pWaveform->ds);
67977 if (result != MA_SUCCESS) {
67978 return result;
67979 }
67980
67981 pWaveform->config = *pConfig;
67982 pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency);
67983 pWaveform->time = 0;
67984
67985 return MA_SUCCESS;
67986}
67987
67988MA_API void ma_waveform_uninit(ma_waveform* pWaveform)
67989{
67990 if (pWaveform == NULL) {
67991 return;
67992 }
67993
67994 ma_data_source_uninit(&pWaveform->ds);
67995}
67996
67997MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude)
67998{
67999 if (pWaveform == NULL) {
68000 return MA_INVALID_ARGS;
68001 }
68002
68003 pWaveform->config.amplitude = amplitude;
68004 return MA_SUCCESS;
68005}
68006
68007MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency)
68008{
68009 if (pWaveform == NULL) {
68010 return MA_INVALID_ARGS;
68011 }
68012
68013 pWaveform->config.frequency = frequency;
68014 ma_waveform__update_advance(pWaveform);
68015
68016 return MA_SUCCESS;
68017}
68018
68019MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type)
68020{
68021 if (pWaveform == NULL) {
68022 return MA_INVALID_ARGS;
68023 }
68024
68025 pWaveform->config.type = type;
68026 return MA_SUCCESS;
68027}
68028
68029MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate)
68030{
68031 if (pWaveform == NULL) {
68032 return MA_INVALID_ARGS;
68033 }
68034
68035 pWaveform->config.sampleRate = sampleRate;
68036 ma_waveform__update_advance(pWaveform);
68037
68038 return MA_SUCCESS;
68039}
68040
68041static float ma_waveform_sine_f32(double time, double amplitude)
68042{
68043 return (float)(ma_sind(MA_TAU_D * time) * amplitude);
68044}
68045
68046static ma_int16 ma_waveform_sine_s16(double time, double amplitude)
68047{
68048 return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude));
68049}
68050
68051static float ma_waveform_square_f32(double time, double dutyCycle, double amplitude)
68052{
68053 double f = time - (ma_int64)time;
68054 double r;
68055
68056 if (f < dutyCycle) {
68057 r = amplitude;
68058 } else {
68059 r = -amplitude;
68060 }
68061
68062 return (float)r;
68063}
68064
68065static ma_int16 ma_waveform_square_s16(double time, double dutyCycle, double amplitude)
68066{
68067 return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, dutyCycle, amplitude));
68068}
68069
68070static float ma_waveform_triangle_f32(double time, double amplitude)
68071{
68072 double f = time - (ma_int64)time;
68073 double r;
68074
68075 r = 2 * ma_abs(2 * (f - 0.5)) - 1;
68076
68077 return (float)(r * amplitude);
68078}
68079
68080static ma_int16 ma_waveform_triangle_s16(double time, double amplitude)
68081{
68082 return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude));
68083}
68084
68085static float ma_waveform_sawtooth_f32(double time, double amplitude)
68086{
68087 double f = time - (ma_int64)time;
68088 double r;
68089
68090 r = 2 * (f - 0.5);
68091
68092 return (float)(r * amplitude);
68093}
68094
68095static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude)
68096{
68097 return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude));
68098}
68099
68100static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
68101{
68102 ma_uint64 iFrame;
68103 ma_uint64 iChannel;
68105 ma_uint32 bpf = bps * pWaveform->config.channels;
68106
68107 MA_ASSERT(pWaveform != NULL);
68108 MA_ASSERT(pFramesOut != NULL);
68109
68110 if (pWaveform->config.format == ma_format_f32) {
68111 float* pFramesOutF32 = (float*)pFramesOut;
68112 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68113 float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude);
68114 pWaveform->time += pWaveform->advance;
68115
68116 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
68117 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
68118 }
68119 }
68120 } else if (pWaveform->config.format == ma_format_s16) {
68121 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
68122 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68123 ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude);
68124 pWaveform->time += pWaveform->advance;
68125
68126 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
68127 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
68128 }
68129 }
68130 } else {
68131 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68132 float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude);
68133 pWaveform->time += pWaveform->advance;
68134
68135 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
68136 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
68137 }
68138 }
68139 }
68140}
68141
68142static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double dutyCycle, void* pFramesOut, ma_uint64 frameCount)
68143{
68144 ma_uint64 iFrame;
68145 ma_uint64 iChannel;
68147 ma_uint32 bpf = bps * pWaveform->config.channels;
68148
68149 MA_ASSERT(pWaveform != NULL);
68150 MA_ASSERT(pFramesOut != NULL);
68151
68152 if (pWaveform->config.format == ma_format_f32) {
68153 float* pFramesOutF32 = (float*)pFramesOut;
68154 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68155 float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude);
68156 pWaveform->time += pWaveform->advance;
68157
68158 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
68159 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
68160 }
68161 }
68162 } else if (pWaveform->config.format == ma_format_s16) {
68163 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
68164 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68165 ma_int16 s = ma_waveform_square_s16(pWaveform->time, dutyCycle, pWaveform->config.amplitude);
68166 pWaveform->time += pWaveform->advance;
68167
68168 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
68169 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
68170 }
68171 }
68172 } else {
68173 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68174 float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude);
68175 pWaveform->time += pWaveform->advance;
68176
68177 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
68178 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
68179 }
68180 }
68181 }
68182}
68183
68184static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
68185{
68186 ma_uint64 iFrame;
68187 ma_uint64 iChannel;
68189 ma_uint32 bpf = bps * pWaveform->config.channels;
68190
68191 MA_ASSERT(pWaveform != NULL);
68192 MA_ASSERT(pFramesOut != NULL);
68193
68194 if (pWaveform->config.format == ma_format_f32) {
68195 float* pFramesOutF32 = (float*)pFramesOut;
68196 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68197 float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude);
68198 pWaveform->time += pWaveform->advance;
68199
68200 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
68201 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
68202 }
68203 }
68204 } else if (pWaveform->config.format == ma_format_s16) {
68205 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
68206 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68207 ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude);
68208 pWaveform->time += pWaveform->advance;
68209
68210 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
68211 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
68212 }
68213 }
68214 } else {
68215 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68216 float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude);
68217 pWaveform->time += pWaveform->advance;
68218
68219 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
68220 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
68221 }
68222 }
68223 }
68224}
68225
68226static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
68227{
68228 ma_uint64 iFrame;
68229 ma_uint64 iChannel;
68231 ma_uint32 bpf = bps * pWaveform->config.channels;
68232
68233 MA_ASSERT(pWaveform != NULL);
68234 MA_ASSERT(pFramesOut != NULL);
68235
68236 if (pWaveform->config.format == ma_format_f32) {
68237 float* pFramesOutF32 = (float*)pFramesOut;
68238 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68239 float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude);
68240 pWaveform->time += pWaveform->advance;
68241
68242 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
68243 pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
68244 }
68245 }
68246 } else if (pWaveform->config.format == ma_format_s16) {
68247 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
68248 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68249 ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude);
68250 pWaveform->time += pWaveform->advance;
68251
68252 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
68253 pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
68254 }
68255 }
68256 } else {
68257 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68258 float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude);
68259 pWaveform->time += pWaveform->advance;
68260
68261 for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
68262 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
68263 }
68264 }
68265 }
68266}
68267
68268MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
68269{
68270 if (pFramesRead != NULL) {
68271 *pFramesRead = 0;
68272 }
68273
68274 if (frameCount == 0) {
68275 return MA_INVALID_ARGS;
68276 }
68277
68278 if (pWaveform == NULL) {
68279 return MA_INVALID_ARGS;
68280 }
68281
68282 if (pFramesOut != NULL) {
68283 switch (pWaveform->config.type)
68284 {
68286 {
68287 ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount);
68288 } break;
68289
68291 {
68292 ma_waveform_read_pcm_frames__square(pWaveform, 0.5, pFramesOut, frameCount);
68293 } break;
68294
68296 {
68297 ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount);
68298 } break;
68299
68301 {
68302 ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount);
68303 } break;
68304
68305 default: return MA_INVALID_OPERATION; /* Unknown waveform type. */
68306 }
68307 } else {
68308 pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */
68309 }
68310
68311 if (pFramesRead != NULL) {
68312 *pFramesRead = frameCount;
68313 }
68314
68315 return MA_SUCCESS;
68316}
68317
68318MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex)
68319{
68320 if (pWaveform == NULL) {
68321 return MA_INVALID_ARGS;
68322 }
68323
68324 pWaveform->time = pWaveform->advance * (ma_int64)frameIndex; /* Casting for VC6. Won't be an issue in practice. */
68325
68326 return MA_SUCCESS;
68327}
68328
68329MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency)
68330{
68331 ma_pulsewave_config config;
68332
68333 MA_ZERO_OBJECT(&config);
68334 config.format = format;
68335 config.channels = channels;
68336 config.sampleRate = sampleRate;
68337 config.dutyCycle = dutyCycle;
68338 config.amplitude = amplitude;
68339 config.frequency = frequency;
68340
68341 return config;
68342}
68343
68344MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform)
68345{
68346 ma_result result;
68347 ma_waveform_config config;
68348
68349 if (pWaveform == NULL) {
68350 return MA_INVALID_ARGS;
68351 }
68352
68353 MA_ZERO_OBJECT(pWaveform);
68354
68355 config = ma_waveform_config_init(
68356 pConfig->format,
68357 pConfig->channels,
68358 pConfig->sampleRate,
68360 pConfig->amplitude,
68361 pConfig->frequency
68362 );
68363
68364 result = ma_waveform_init(&config, &pWaveform->waveform);
68365 ma_pulsewave_set_duty_cycle(pWaveform, pConfig->dutyCycle);
68366
68367 return result;
68368}
68369
68370MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform)
68371{
68372 if (pWaveform == NULL) {
68373 return;
68374 }
68375
68376 ma_waveform_uninit(&pWaveform->waveform);
68377}
68378
68379MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
68380{
68381 if (pFramesRead != NULL) {
68382 *pFramesRead = 0;
68383 }
68384
68385 if (frameCount == 0) {
68386 return MA_INVALID_ARGS;
68387 }
68388
68389 if (pWaveform == NULL) {
68390 return MA_INVALID_ARGS;
68391 }
68392
68393 if (pFramesOut != NULL) {
68394 ma_waveform_read_pcm_frames__square(&pWaveform->waveform, pWaveform->config.dutyCycle, pFramesOut, frameCount);
68395 } else {
68396 pWaveform->waveform.time += pWaveform->waveform.advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */
68397 }
68398
68399 if (pFramesRead != NULL) {
68400 *pFramesRead = frameCount;
68401 }
68402
68403 return MA_SUCCESS;
68404}
68405
68406MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex)
68407{
68408 if (pWaveform == NULL) {
68409 return MA_INVALID_ARGS;
68410 }
68411
68412 ma_waveform_seek_to_pcm_frame(&pWaveform->waveform, frameIndex);
68413
68414 return MA_SUCCESS;
68415}
68416
68417MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude)
68418{
68419 if (pWaveform == NULL) {
68420 return MA_INVALID_ARGS;
68421 }
68422
68423 pWaveform->config.amplitude = amplitude;
68424 ma_waveform_set_amplitude(&pWaveform->waveform, amplitude);
68425
68426 return MA_SUCCESS;
68427}
68428
68429MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency)
68430{
68431 if (pWaveform == NULL) {
68432 return MA_INVALID_ARGS;
68433 }
68434
68435 pWaveform->config.frequency = frequency;
68436 ma_waveform_set_frequency(&pWaveform->waveform, frequency);
68437
68438 return MA_SUCCESS;
68439}
68440
68441MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate)
68442{
68443 if (pWaveform == NULL) {
68444 return MA_INVALID_ARGS;
68445 }
68446
68447 pWaveform->config.sampleRate = sampleRate;
68448 ma_waveform_set_sample_rate(&pWaveform->waveform, sampleRate);
68449
68450 return MA_SUCCESS;
68451}
68452
68453MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle)
68454{
68455 if (pWaveform == NULL) {
68456 return MA_INVALID_ARGS;
68457 }
68458
68459 pWaveform->config.dutyCycle = dutyCycle;
68460
68461 return MA_SUCCESS;
68462}
68463
68464
68465
68466MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude)
68467{
68468 ma_noise_config config;
68469 MA_ZERO_OBJECT(&config);
68470
68471 config.format = format;
68472 config.channels = channels;
68473 config.type = type;
68474 config.seed = seed;
68475 config.amplitude = amplitude;
68476
68477 if (config.seed == 0) {
68478 config.seed = MA_DEFAULT_LCG_SEED;
68479 }
68480
68481 return config;
68482}
68483
68484
68485static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
68486{
68487 return ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount, pFramesRead);
68488}
68489
68490static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
68491{
68492 /* No-op. Just pretend to be successful. */
68493 (void)pDataSource;
68494 (void)frameIndex;
68495 return MA_SUCCESS;
68496}
68497
68498static 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)
68499{
68500 ma_noise* pNoise = (ma_noise*)pDataSource;
68501
68502 *pFormat = pNoise->config.format;
68503 *pChannels = pNoise->config.channels;
68504 *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */
68506
68507 return MA_SUCCESS;
68508}
68509
68510static ma_data_source_vtable g_ma_noise_data_source_vtable =
68511{
68512 ma_noise__data_source_on_read,
68513 ma_noise__data_source_on_seek, /* No-op for noise. */
68514 ma_noise__data_source_on_get_data_format,
68515 NULL, /* onGetCursor. No notion of a cursor for noise. */
68516 NULL, /* onGetLength. No notion of a length for noise. */
68517 NULL, /* onSetLooping */
68518 0
68519};
68520
68521
68522#ifndef MA_PINK_NOISE_BIN_SIZE
68523#define MA_PINK_NOISE_BIN_SIZE 16
68524#endif
68525
68526typedef struct
68527{
68528 size_t sizeInBytes;
68529 struct
68530 {
68531 size_t binOffset;
68532 size_t accumulationOffset;
68533 size_t counterOffset;
68534 } pink;
68535 struct
68536 {
68537 size_t accumulationOffset;
68538 } brownian;
68539} ma_noise_heap_layout;
68540
68541static ma_result ma_noise_get_heap_layout(const ma_noise_config* pConfig, ma_noise_heap_layout* pHeapLayout)
68542{
68543 MA_ASSERT(pHeapLayout != NULL);
68544
68545 MA_ZERO_OBJECT(pHeapLayout);
68546
68547 if (pConfig == NULL) {
68548 return MA_INVALID_ARGS;
68549 }
68550
68551 if (pConfig->channels == 0) {
68552 return MA_INVALID_ARGS;
68553 }
68554
68555 pHeapLayout->sizeInBytes = 0;
68556
68557 /* Pink. */
68558 if (pConfig->type == ma_noise_type_pink) {
68559 /* bin */
68560 pHeapLayout->pink.binOffset = pHeapLayout->sizeInBytes;
68561 pHeapLayout->sizeInBytes += sizeof(double*) * pConfig->channels;
68562 pHeapLayout->sizeInBytes += sizeof(double ) * pConfig->channels * MA_PINK_NOISE_BIN_SIZE;
68563
68564 /* accumulation */
68565 pHeapLayout->pink.accumulationOffset = pHeapLayout->sizeInBytes;
68566 pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels;
68567
68568 /* counter */
68569 pHeapLayout->pink.counterOffset = pHeapLayout->sizeInBytes;
68570 pHeapLayout->sizeInBytes += sizeof(ma_uint32) * pConfig->channels;
68571 }
68572
68573 /* Brownian. */
68574 if (pConfig->type == ma_noise_type_brownian) {
68575 /* accumulation */
68576 pHeapLayout->brownian.accumulationOffset = pHeapLayout->sizeInBytes;
68577 pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels;
68578 }
68579
68580 /* Make sure allocation size is aligned. */
68581 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
68582
68583 return MA_SUCCESS;
68584}
68585
68586MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes)
68587{
68588 ma_result result;
68589 ma_noise_heap_layout heapLayout;
68590
68591 if (pHeapSizeInBytes == NULL) {
68592 return MA_INVALID_ARGS;
68593 }
68594
68595 *pHeapSizeInBytes = 0;
68596
68597 result = ma_noise_get_heap_layout(pConfig, &heapLayout);
68598 if (result != MA_SUCCESS) {
68599 return result;
68600 }
68601
68602 *pHeapSizeInBytes = heapLayout.sizeInBytes;
68603
68604 return MA_SUCCESS;
68605}
68606
68607MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise)
68608{
68609 ma_result result;
68610 ma_noise_heap_layout heapLayout;
68611 ma_data_source_config dataSourceConfig;
68612 ma_uint32 iChannel;
68613
68614 if (pNoise == NULL) {
68615 return MA_INVALID_ARGS;
68616 }
68617
68618 MA_ZERO_OBJECT(pNoise);
68619
68620 result = ma_noise_get_heap_layout(pConfig, &heapLayout);
68621 if (result != MA_SUCCESS) {
68622 return result;
68623 }
68624
68625 pNoise->_pHeap = pHeap;
68626 MA_ZERO_MEMORY(pNoise->_pHeap, heapLayout.sizeInBytes);
68627
68628 dataSourceConfig = ma_data_source_config_init();
68629 dataSourceConfig.vtable = &g_ma_noise_data_source_vtable;
68630
68631 result = ma_data_source_init(&dataSourceConfig, &pNoise->ds);
68632 if (result != MA_SUCCESS) {
68633 return result;
68634 }
68635
68636 pNoise->config = *pConfig;
68637 ma_lcg_seed(&pNoise->lcg, pConfig->seed);
68638
68639 if (pNoise->config.type == ma_noise_type_pink) {
68640 pNoise->state.pink.bin = (double** )ma_offset_ptr(pHeap, heapLayout.pink.binOffset);
68641 pNoise->state.pink.accumulation = (double* )ma_offset_ptr(pHeap, heapLayout.pink.accumulationOffset);
68642 pNoise->state.pink.counter = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.pink.counterOffset);
68643
68644 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
68645 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));
68646 pNoise->state.pink.accumulation[iChannel] = 0;
68647 pNoise->state.pink.counter[iChannel] = 1;
68648 }
68649 }
68650
68651 if (pNoise->config.type == ma_noise_type_brownian) {
68652 pNoise->state.brownian.accumulation = (double*)ma_offset_ptr(pHeap, heapLayout.brownian.accumulationOffset);
68653
68654 for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
68655 pNoise->state.brownian.accumulation[iChannel] = 0;
68656 }
68657 }
68658
68659 return MA_SUCCESS;
68660}
68661
68662MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise)
68663{
68664 ma_result result;
68665 size_t heapSizeInBytes;
68666 void* pHeap;
68667
68668 result = ma_noise_get_heap_size(pConfig, &heapSizeInBytes);
68669 if (result != MA_SUCCESS) {
68670 return result;
68671 }
68672
68673 if (heapSizeInBytes > 0) {
68674 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
68675 if (pHeap == NULL) {
68676 return MA_OUT_OF_MEMORY;
68677 }
68678 } else {
68679 pHeap = NULL;
68680 }
68681
68682 result = ma_noise_init_preallocated(pConfig, pHeap, pNoise);
68683 if (result != MA_SUCCESS) {
68684 ma_free(pHeap, pAllocationCallbacks);
68685 return result;
68686 }
68687
68688 pNoise->_ownsHeap = MA_TRUE;
68689 return MA_SUCCESS;
68690}
68691
68692MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks)
68693{
68694 if (pNoise == NULL) {
68695 return;
68696 }
68697
68698 ma_data_source_uninit(&pNoise->ds);
68699
68700 if (pNoise->_ownsHeap) {
68701 ma_free(pNoise->_pHeap, pAllocationCallbacks);
68702 }
68703}
68704
68705MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude)
68706{
68707 if (pNoise == NULL) {
68708 return MA_INVALID_ARGS;
68709 }
68710
68711 pNoise->config.amplitude = amplitude;
68712 return MA_SUCCESS;
68713}
68714
68715MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed)
68716{
68717 if (pNoise == NULL) {
68718 return MA_INVALID_ARGS;
68719 }
68720
68721 pNoise->lcg.state = seed;
68722 return MA_SUCCESS;
68723}
68724
68725
68726MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type)
68727{
68728 if (pNoise == NULL) {
68729 return MA_INVALID_ARGS;
68730 }
68731
68732 /*
68733 This function should never have been implemented in the first place. Changing the type dynamically is not
68734 supported. Instead you need to uninitialize and reinitialize a fresh `ma_noise` object. This function
68735 will be removed in version 0.12.
68736 */
68737 MA_ASSERT(MA_FALSE);
68738 (void)type;
68739
68740 return MA_INVALID_OPERATION;
68741}
68742
68743static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise)
68744{
68745 return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude);
68746}
68747
68748static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise)
68749{
68750 return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise));
68751}
68752
68753static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
68754{
68755 ma_uint64 iFrame;
68756 ma_uint32 iChannel;
68757 const ma_uint32 channels = pNoise->config.channels;
68758 MA_ASSUME(channels > 0);
68759
68760 if (pNoise->config.format == ma_format_f32) {
68761 float* pFramesOutF32 = (float*)pFramesOut;
68762 if (pNoise->config.duplicateChannels) {
68763 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68764 float s = ma_noise_f32_white(pNoise);
68765 for (iChannel = 0; iChannel < channels; iChannel += 1) {
68766 pFramesOutF32[iFrame*channels + iChannel] = s;
68767 }
68768 }
68769 } else {
68770 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68771 for (iChannel = 0; iChannel < channels; iChannel += 1) {
68772 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise);
68773 }
68774 }
68775 }
68776 } else if (pNoise->config.format == ma_format_s16) {
68777 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
68778 if (pNoise->config.duplicateChannels) {
68779 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68780 ma_int16 s = ma_noise_s16_white(pNoise);
68781 for (iChannel = 0; iChannel < channels; iChannel += 1) {
68782 pFramesOutS16[iFrame*channels + iChannel] = s;
68783 }
68784 }
68785 } else {
68786 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68787 for (iChannel = 0; iChannel < channels; iChannel += 1) {
68788 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise);
68789 }
68790 }
68791 }
68792 } else {
68793 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
68794 const ma_uint32 bpf = bps * channels;
68795
68796 if (pNoise->config.duplicateChannels) {
68797 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68798 float s = ma_noise_f32_white(pNoise);
68799 for (iChannel = 0; iChannel < channels; iChannel += 1) {
68800 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
68801 }
68802 }
68803 } else {
68804 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68805 for (iChannel = 0; iChannel < channels; iChannel += 1) {
68806 float s = ma_noise_f32_white(pNoise);
68807 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
68808 }
68809 }
68810 }
68811 }
68812
68813 return frameCount;
68814}
68815
68816
68817static MA_INLINE unsigned int ma_tzcnt32(unsigned int x)
68818{
68819 unsigned int n;
68820
68821 /* Special case for odd numbers since they should happen about half the time. */
68822 if (x & 0x1) {
68823 return 0;
68824 }
68825
68826 if (x == 0) {
68827 return sizeof(x) << 3;
68828 }
68829
68830 n = 1;
68831 if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; }
68832 if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; }
68833 if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; }
68834 if ((x & 0x00000003) == 0) { x >>= 2; n += 2; }
68835 n -= x & 0x00000001;
68836
68837 return n;
68838}
68839
68840/*
68841Pink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h
68842
68843This is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/
68844*/
68845static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel)
68846{
68847 double result;
68848 double binPrev;
68849 double binNext;
68850 unsigned int ibin;
68851
68852 ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (MA_PINK_NOISE_BIN_SIZE - 1);
68853
68854 binPrev = pNoise->state.pink.bin[iChannel][ibin];
68855 binNext = ma_lcg_rand_f64(&pNoise->lcg);
68856 pNoise->state.pink.bin[iChannel][ibin] = binNext;
68857
68858 pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev);
68859 pNoise->state.pink.counter[iChannel] += 1;
68860
68861 result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]);
68862 result /= 10;
68863
68864 return (float)(result * pNoise->config.amplitude);
68865}
68866
68867static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel)
68868{
68869 return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel));
68870}
68871
68872static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
68873{
68874 ma_uint64 iFrame;
68875 ma_uint32 iChannel;
68876 const ma_uint32 channels = pNoise->config.channels;
68877 MA_ASSUME(channels > 0);
68878
68879 if (pNoise->config.format == ma_format_f32) {
68880 float* pFramesOutF32 = (float*)pFramesOut;
68881 if (pNoise->config.duplicateChannels) {
68882 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68883 float s = ma_noise_f32_pink(pNoise, 0);
68884 for (iChannel = 0; iChannel < channels; iChannel += 1) {
68885 pFramesOutF32[iFrame*channels + iChannel] = s;
68886 }
68887 }
68888 } else {
68889 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68890 for (iChannel = 0; iChannel < channels; iChannel += 1) {
68891 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel);
68892 }
68893 }
68894 }
68895 } else if (pNoise->config.format == ma_format_s16) {
68896 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
68897 if (pNoise->config.duplicateChannels) {
68898 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68899 ma_int16 s = ma_noise_s16_pink(pNoise, 0);
68900 for (iChannel = 0; iChannel < channels; iChannel += 1) {
68901 pFramesOutS16[iFrame*channels + iChannel] = s;
68902 }
68903 }
68904 } else {
68905 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68906 for (iChannel = 0; iChannel < channels; iChannel += 1) {
68907 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel);
68908 }
68909 }
68910 }
68911 } else {
68912 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
68913 const ma_uint32 bpf = bps * channels;
68914
68915 if (pNoise->config.duplicateChannels) {
68916 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68917 float s = ma_noise_f32_pink(pNoise, 0);
68918 for (iChannel = 0; iChannel < channels; iChannel += 1) {
68919 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
68920 }
68921 }
68922 } else {
68923 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68924 for (iChannel = 0; iChannel < channels; iChannel += 1) {
68925 float s = ma_noise_f32_pink(pNoise, iChannel);
68926 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
68927 }
68928 }
68929 }
68930 }
68931
68932 return frameCount;
68933}
68934
68935
68936static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel)
68937{
68938 double result;
68939
68940 result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]);
68941 result /= 1.005; /* Don't escape the -1..1 range on average. */
68942
68943 pNoise->state.brownian.accumulation[iChannel] = result;
68944 result /= 20;
68945
68946 return (float)(result * pNoise->config.amplitude);
68947}
68948
68949static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel)
68950{
68951 return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel));
68952}
68953
68954static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
68955{
68956 ma_uint64 iFrame;
68957 ma_uint32 iChannel;
68958 const ma_uint32 channels = pNoise->config.channels;
68959 MA_ASSUME(channels > 0);
68960
68961 if (pNoise->config.format == ma_format_f32) {
68962 float* pFramesOutF32 = (float*)pFramesOut;
68963 if (pNoise->config.duplicateChannels) {
68964 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68965 float s = ma_noise_f32_brownian(pNoise, 0);
68966 for (iChannel = 0; iChannel < channels; iChannel += 1) {
68967 pFramesOutF32[iFrame*channels + iChannel] = s;
68968 }
68969 }
68970 } else {
68971 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68972 for (iChannel = 0; iChannel < channels; iChannel += 1) {
68973 pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel);
68974 }
68975 }
68976 }
68977 } else if (pNoise->config.format == ma_format_s16) {
68978 ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
68979 if (pNoise->config.duplicateChannels) {
68980 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68981 ma_int16 s = ma_noise_s16_brownian(pNoise, 0);
68982 for (iChannel = 0; iChannel < channels; iChannel += 1) {
68983 pFramesOutS16[iFrame*channels + iChannel] = s;
68984 }
68985 }
68986 } else {
68987 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68988 for (iChannel = 0; iChannel < channels; iChannel += 1) {
68989 pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel);
68990 }
68991 }
68992 }
68993 } else {
68994 const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
68995 const ma_uint32 bpf = bps * channels;
68996
68997 if (pNoise->config.duplicateChannels) {
68998 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
68999 float s = ma_noise_f32_brownian(pNoise, 0);
69000 for (iChannel = 0; iChannel < channels; iChannel += 1) {
69001 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
69002 }
69003 }
69004 } else {
69005 for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
69006 for (iChannel = 0; iChannel < channels; iChannel += 1) {
69007 float s = ma_noise_f32_brownian(pNoise, iChannel);
69008 ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
69009 }
69010 }
69011 }
69012 }
69013
69014 return frameCount;
69015}
69016
69017MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
69018{
69019 ma_uint64 framesRead = 0;
69020
69021 if (pFramesRead != NULL) {
69022 *pFramesRead = 0;
69023 }
69024
69025 if (frameCount == 0) {
69026 return MA_INVALID_ARGS;
69027 }
69028
69029 if (pNoise == NULL) {
69030 return MA_INVALID_ARGS;
69031 }
69032
69033 /* 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. */
69034 if (pFramesOut == NULL) {
69035 framesRead = frameCount;
69036 } else {
69037 switch (pNoise->config.type) {
69038 case ma_noise_type_white: framesRead = ma_noise_read_pcm_frames__white (pNoise, pFramesOut, frameCount); break;
69039 case ma_noise_type_pink: framesRead = ma_noise_read_pcm_frames__pink (pNoise, pFramesOut, frameCount); break;
69040 case ma_noise_type_brownian: framesRead = ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); break;
69041 default: return MA_INVALID_OPERATION; /* Unknown noise type. */
69042 }
69043 }
69044
69045 if (pFramesRead != NULL) {
69046 *pFramesRead = framesRead;
69047 }
69048
69049 return MA_SUCCESS;
69050}
69051#endif /* MA_NO_GENERATION */
69052
69053
69054
69055#ifndef MA_NO_RESOURCE_MANAGER
69056#ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS
69057#define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 1000
69058#endif
69059
69060#ifndef MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY
69061#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY 1024
69062#endif
69063
69064MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void)
69065{
69066 ma_resource_manager_pipeline_notifications notifications;
69067
69068 MA_ZERO_OBJECT(&notifications);
69069
69070 return notifications;
69071}
69072
69073static void ma_resource_manager_pipeline_notifications_signal_all_notifications(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)
69074{
69075 if (pPipelineNotifications == NULL) {
69076 return;
69077 }
69078
69079 if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); }
69080 if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); }
69081}
69082
69083static void ma_resource_manager_pipeline_notifications_acquire_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)
69084{
69085 if (pPipelineNotifications == NULL) {
69086 return;
69087 }
69088
69089 if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); }
69090 if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); }
69091}
69092
69093static void ma_resource_manager_pipeline_notifications_release_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)
69094{
69095 if (pPipelineNotifications == NULL) {
69096 return;
69097 }
69098
69099 if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); }
69100 if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); }
69101}
69102
69103
69104
69105#ifndef MA_DEFAULT_HASH_SEED
69106#define MA_DEFAULT_HASH_SEED 42
69107#endif
69108
69109/* MurmurHash3. Based on code from https://github.com/PeterScott/murmur3/blob/master/murmur3.c (public domain). */
69110#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
69111 #pragma GCC diagnostic push
69112 #if __GNUC__ >= 7
69113 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
69114 #endif
69115#endif
69116
69117static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r)
69118{
69119 return (x << r) | (x >> (32 - r));
69120}
69121
69122static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i)
69123{
69124 ma_uint32 block;
69125
69126 /* Try silencing a sanitization warning about unaligned access by doing a memcpy() instead of assignment. */
69127 MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * sizeof(block)), sizeof(block));
69128
69129 if (ma_is_little_endian()) {
69130 return block;
69131 } else {
69132 return ma_swap_endian_uint32(block);
69133 }
69134}
69135
69136static MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h)
69137{
69138 h ^= h >> 16;
69139 h *= 0x85ebca6b;
69140 h ^= h >> 13;
69141 h *= 0xc2b2ae35;
69142 h ^= h >> 16;
69143
69144 return h;
69145}
69146
69147static ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed)
69148{
69149 const ma_uint8* data = (const ma_uint8*)key;
69150 const ma_uint32* blocks;
69151 const ma_uint8* tail;
69152 const int nblocks = len / 4;
69153 ma_uint32 h1 = seed;
69154 ma_uint32 c1 = 0xcc9e2d51;
69155 ma_uint32 c2 = 0x1b873593;
69156 ma_uint32 k1;
69157 int i;
69158
69159 blocks = (const ma_uint32 *)(data + nblocks*4);
69160
69161 for(i = -nblocks; i; i++) {
69162 k1 = ma_hash_getblock(blocks,i);
69163
69164 k1 *= c1;
69165 k1 = ma_rotl32(k1, 15);
69166 k1 *= c2;
69167
69168 h1 ^= k1;
69169 h1 = ma_rotl32(h1, 13);
69170 h1 = h1*5 + 0xe6546b64;
69171 }
69172
69173
69174 tail = (const ma_uint8*)(data + nblocks*4);
69175
69176 k1 = 0;
69177 switch(len & 3) {
69178 case 3: k1 ^= tail[2] << 16;
69179 case 2: k1 ^= tail[1] << 8;
69180 case 1: k1 ^= tail[0];
69181 k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1;
69182 };
69183
69184
69185 h1 ^= len;
69186 h1 = ma_hash_fmix32(h1);
69187
69188 return h1;
69189}
69190
69191#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
69192 #pragma GCC diagnostic push
69193#endif
69194/* End MurmurHash3 */
69195
69196static ma_uint32 ma_hash_string_32(const char* str)
69197{
69198 return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED);
69199}
69200
69201static ma_uint32 ma_hash_string_w_32(const wchar_t* str)
69202{
69203 return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED);
69204}
69205
69206
69207
69208
69209/*
69210Basic BST Functions
69211*/
69212static ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode)
69213{
69215
69216 MA_ASSERT(pResourceManager != NULL);
69217 MA_ASSERT(ppDataBufferNode != NULL);
69218
69219 pCurrentNode = pResourceManager->pRootDataBufferNode;
69220 while (pCurrentNode != NULL) {
69221 if (hashedName32 == pCurrentNode->hashedName32) {
69222 break; /* Found. */
69223 } else if (hashedName32 < pCurrentNode->hashedName32) {
69224 pCurrentNode = pCurrentNode->pChildLo;
69225 } else {
69226 pCurrentNode = pCurrentNode->pChildHi;
69227 }
69228 }
69229
69230 *ppDataBufferNode = pCurrentNode;
69231
69232 if (pCurrentNode == NULL) {
69233 return MA_DOES_NOT_EXIST;
69234 } else {
69235 return MA_SUCCESS;
69236 }
69237}
69238
69239static ma_result ma_resource_manager_data_buffer_node_insert_point(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppInsertPoint)
69240{
69241 ma_result result = MA_SUCCESS;
69243
69244 MA_ASSERT(pResourceManager != NULL);
69245 MA_ASSERT(ppInsertPoint != NULL);
69246
69247 *ppInsertPoint = NULL;
69248
69249 if (pResourceManager->pRootDataBufferNode == NULL) {
69250 return MA_SUCCESS; /* No items. */
69251 }
69252
69253 /* 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. */
69254 pCurrentNode = pResourceManager->pRootDataBufferNode;
69255 while (pCurrentNode != NULL) {
69256 if (hashedName32 == pCurrentNode->hashedName32) {
69257 result = MA_ALREADY_EXISTS;
69258 break;
69259 } else {
69260 if (hashedName32 < pCurrentNode->hashedName32) {
69261 if (pCurrentNode->pChildLo == NULL) {
69262 result = MA_SUCCESS;
69263 break;
69264 } else {
69265 pCurrentNode = pCurrentNode->pChildLo;
69266 }
69267 } else {
69268 if (pCurrentNode->pChildHi == NULL) {
69269 result = MA_SUCCESS;
69270 break;
69271 } else {
69272 pCurrentNode = pCurrentNode->pChildHi;
69273 }
69274 }
69275 }
69276 }
69277
69278 *ppInsertPoint = pCurrentNode;
69279 return result;
69280}
69281
69282static 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)
69283{
69284 MA_ASSERT(pResourceManager != NULL);
69285 MA_ASSERT(pDataBufferNode != NULL);
69286
69287 /* The key must have been set before calling this function. */
69288 MA_ASSERT(pDataBufferNode->hashedName32 != 0);
69289
69290 if (pInsertPoint == NULL) {
69291 /* It's the first node. */
69292 pResourceManager->pRootDataBufferNode = pDataBufferNode;
69293 } else {
69294 /* It's not the first node. It needs to be inserted. */
69295 if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) {
69296 MA_ASSERT(pInsertPoint->pChildLo == NULL);
69297 pInsertPoint->pChildLo = pDataBufferNode;
69298 } else {
69299 MA_ASSERT(pInsertPoint->pChildHi == NULL);
69300 pInsertPoint->pChildHi = pDataBufferNode;
69301 }
69302 }
69303
69304 pDataBufferNode->pParent = pInsertPoint;
69305
69306 return MA_SUCCESS;
69307}
69308
69309#if 0 /* Unused for now. */
69310static ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
69311{
69312 ma_result result;
69314
69315 MA_ASSERT(pResourceManager != NULL);
69316 MA_ASSERT(pDataBufferNode != NULL);
69317
69318 result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint);
69319 if (result != MA_SUCCESS) {
69320 return MA_INVALID_ARGS;
69321 }
69322
69323 return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint);
69324}
69325#endif
69326
69327static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode)
69328{
69330
69331 MA_ASSERT(pDataBufferNode != NULL);
69332
69333 pCurrentNode = pDataBufferNode;
69334 while (pCurrentNode->pChildLo != NULL) {
69335 pCurrentNode = pCurrentNode->pChildLo;
69336 }
69337
69338 return pCurrentNode;
69339}
69340
69341static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode)
69342{
69344
69345 MA_ASSERT(pDataBufferNode != NULL);
69346
69347 pCurrentNode = pDataBufferNode;
69348 while (pCurrentNode->pChildHi != NULL) {
69349 pCurrentNode = pCurrentNode->pChildHi;
69350 }
69351
69352 return pCurrentNode;
69353}
69354
69355static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_successor(ma_resource_manager_data_buffer_node* pDataBufferNode)
69356{
69357 MA_ASSERT(pDataBufferNode != NULL);
69358 MA_ASSERT(pDataBufferNode->pChildHi != NULL);
69359
69360 return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi);
69361}
69362
69363#if 0 /* Currently unused, but might make use of this later. */
69364static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode)
69365{
69366 MA_ASSERT(pDataBufferNode != NULL);
69367 MA_ASSERT(pDataBufferNode->pChildLo != NULL);
69368
69369 return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo);
69370}
69371#endif
69372
69373static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
69374{
69375 MA_ASSERT(pResourceManager != NULL);
69376 MA_ASSERT(pDataBufferNode != NULL);
69377
69378 if (pDataBufferNode->pChildLo == NULL) {
69379 if (pDataBufferNode->pChildHi == NULL) {
69380 /* Simple case - deleting a buffer with no children. */
69381 if (pDataBufferNode->pParent == NULL) {
69382 MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); /* There is only a single buffer in the tree which should be equal to the root node. */
69383 pResourceManager->pRootDataBufferNode = NULL;
69384 } else {
69385 if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
69386 pDataBufferNode->pParent->pChildLo = NULL;
69387 } else {
69388 pDataBufferNode->pParent->pChildHi = NULL;
69389 }
69390 }
69391 } else {
69392 /* Node has one child - pChildHi != NULL. */
69393 pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent;
69394
69395 if (pDataBufferNode->pParent == NULL) {
69396 MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);
69397 pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi;
69398 } else {
69399 if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
69400 pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi;
69401 } else {
69402 pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi;
69403 }
69404 }
69405 }
69406 } else {
69407 if (pDataBufferNode->pChildHi == NULL) {
69408 /* Node has one child - pChildLo != NULL. */
69409 pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent;
69410
69411 if (pDataBufferNode->pParent == NULL) {
69412 MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);
69413 pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo;
69414 } else {
69415 if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
69416 pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo;
69417 } else {
69418 pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo;
69419 }
69420 }
69421 } else {
69422 /* Complex case - deleting a node with two children. */
69423 ma_resource_manager_data_buffer_node* pReplacementDataBufferNode;
69424
69425 /* 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. */
69426 pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode);
69427 MA_ASSERT(pReplacementDataBufferNode != NULL);
69428
69429 /*
69430 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
69431 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
69432 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
69433 replacement node and reinserting it into the same position as the deleted node.
69434 */
69435 MA_ASSERT(pReplacementDataBufferNode->pParent != NULL); /* The replacement node should never be the root which means it should always have a parent. */
69436 MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL); /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */
69437
69438 if (pReplacementDataBufferNode->pChildHi == NULL) {
69439 if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) {
69440 pReplacementDataBufferNode->pParent->pChildLo = NULL;
69441 } else {
69442 pReplacementDataBufferNode->pParent->pChildHi = NULL;
69443 }
69444 } else {
69445 pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent;
69446 if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) {
69447 pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi;
69448 } else {
69449 pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi;
69450 }
69451 }
69452
69453
69454 /* 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 */
69455 if (pDataBufferNode->pParent != NULL) {
69456 if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
69457 pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode;
69458 } else {
69459 pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode;
69460 }
69461 }
69462
69463 /* Now need to update the replacement node's pointers. */
69464 pReplacementDataBufferNode->pParent = pDataBufferNode->pParent;
69465 pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo;
69466 pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi;
69467
69468 /* Now the children of the replacement node need to have their parent pointers updated. */
69469 if (pReplacementDataBufferNode->pChildLo != NULL) {
69470 pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode;
69471 }
69472 if (pReplacementDataBufferNode->pChildHi != NULL) {
69473 pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode;
69474 }
69475
69476 /* Now the root node needs to be updated. */
69477 if (pResourceManager->pRootDataBufferNode == pDataBufferNode) {
69478 pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode;
69479 }
69480 }
69481 }
69482
69483 return MA_SUCCESS;
69484}
69485
69486#if 0 /* Unused for now. */
69487static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32)
69488{
69489 ma_result result;
69490 ma_resource_manager_data_buffer_node* pDataBufferNode;
69491
69492 result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode);
69493 if (result != MA_SUCCESS) {
69494 return result; /* Could not find the data buffer. */
69495 }
69496
69497 return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode);
69498}
69499#endif
69500
69501static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode)
69502{
69503 return (ma_resource_manager_data_supply_type)ma_atomic_load_i32(&pDataBufferNode->data.type);
69504}
69505
69506static 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)
69507{
69508 ma_atomic_exchange_i32(&pDataBufferNode->data.type, supplyType);
69509}
69510
69511static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount)
69512{
69513 ma_uint32 refCount;
69514
69515 MA_ASSERT(pResourceManager != NULL);
69516 MA_ASSERT(pDataBufferNode != NULL);
69517
69518 (void)pResourceManager;
69519
69520 refCount = ma_atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1;
69521
69522 if (pNewRefCount != NULL) {
69523 *pNewRefCount = refCount;
69524 }
69525
69526 return MA_SUCCESS;
69527}
69528
69529static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount)
69530{
69531 ma_uint32 refCount;
69532
69533 MA_ASSERT(pResourceManager != NULL);
69534 MA_ASSERT(pDataBufferNode != NULL);
69535
69536 (void)pResourceManager;
69537
69538 refCount = ma_atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1;
69539
69540 if (pNewRefCount != NULL) {
69541 *pNewRefCount = refCount;
69542 }
69543
69544 return MA_SUCCESS;
69545}
69546
69547static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
69548{
69549 MA_ASSERT(pResourceManager != NULL);
69550 MA_ASSERT(pDataBufferNode != NULL);
69551
69552 if (pDataBufferNode->isDataOwnedByResourceManager) {
69553 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) {
69554 ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks);
69555 pDataBufferNode->data.backend.encoded.pData = NULL;
69556 pDataBufferNode->data.backend.encoded.sizeInBytes = 0;
69557 } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) {
69558 ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks);
69559 pDataBufferNode->data.backend.decoded.pData = NULL;
69560 pDataBufferNode->data.backend.decoded.totalFrameCount = 0;
69561 } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) {
69563 } else {
69564 /* Should never hit this if the node was successfully initialized. */
69565 MA_ASSERT(pDataBufferNode->result != MA_SUCCESS);
69566 }
69567 }
69568
69569 /* The data buffer itself needs to be freed. */
69570 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
69571}
69572
69573static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode)
69574{
69575 MA_ASSERT(pDataBufferNode != NULL);
69576
69577 return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */
69578}
69579
69580
69581static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager)
69582{
69583 MA_ASSERT(pResourceManager != NULL);
69584
69585 return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0;
69586}
69587
69588
69589typedef struct
69590{
69591 union
69592 {
69593 ma_async_notification_event e;
69594 ma_async_notification_poll p;
69595 } backend; /* Must be the first member. */
69596 ma_resource_manager* pResourceManager;
69597} ma_resource_manager_inline_notification;
69598
69599static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification)
69600{
69601 MA_ASSERT(pResourceManager != NULL);
69602 MA_ASSERT(pNotification != NULL);
69603
69604 pNotification->pResourceManager = pResourceManager;
69605
69606 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
69607 return ma_async_notification_event_init(&pNotification->backend.e);
69608 } else {
69609 return ma_async_notification_poll_init(&pNotification->backend.p);
69610 }
69611}
69612
69613static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification)
69614{
69615 MA_ASSERT(pNotification != NULL);
69616
69617 if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) {
69618 ma_async_notification_event_uninit(&pNotification->backend.e);
69619 } else {
69620 /* No need to uninitialize a polling notification. */
69621 }
69622}
69623
69624static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification)
69625{
69626 MA_ASSERT(pNotification != NULL);
69627
69628 if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) {
69629 ma_async_notification_event_wait(&pNotification->backend.e);
69630 } else {
69631 while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) {
69632 ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager);
69633 if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) {
69634 break;
69635 }
69636 }
69637 }
69638}
69639
69640static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification)
69641{
69642 ma_resource_manager_inline_notification_wait(pNotification);
69643 ma_resource_manager_inline_notification_uninit(pNotification);
69644}
69645
69646
69647static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager)
69648{
69649 MA_ASSERT(pResourceManager != NULL);
69650
69651 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
69652 #ifndef MA_NO_THREADING
69653 {
69654 ma_mutex_lock(&pResourceManager->dataBufferBSTLock);
69655 }
69656 #else
69657 {
69658 MA_ASSERT(MA_FALSE); /* Should never hit this. */
69659 }
69660 #endif
69661 } else {
69662 /* Threading not enabled. Do nothing. */
69663 }
69664}
69665
69666static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager)
69667{
69668 MA_ASSERT(pResourceManager != NULL);
69669
69670 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
69671 #ifndef MA_NO_THREADING
69672 {
69673 ma_mutex_unlock(&pResourceManager->dataBufferBSTLock);
69674 }
69675 #else
69676 {
69677 MA_ASSERT(MA_FALSE); /* Should never hit this. */
69678 }
69679 #endif
69680 } else {
69681 /* Threading not enabled. Do nothing. */
69682 }
69683}
69684
69685#ifndef MA_NO_THREADING
69686static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData)
69687{
69688 ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData;
69689 MA_ASSERT(pResourceManager != NULL);
69690
69691 for (;;) {
69692 ma_result result;
69693 ma_job job;
69694
69695 result = ma_resource_manager_next_job(pResourceManager, &job);
69696 if (result != MA_SUCCESS) {
69697 break;
69698 }
69699
69700 /* Terminate if we got a quit message. */
69701 if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) {
69702 break;
69703 }
69704
69705 ma_job_process(&job);
69706 }
69707
69708 return (ma_thread_result)0;
69709}
69710#endif
69711
69712MA_API ma_resource_manager_config ma_resource_manager_config_init(void)
69713{
69714 ma_resource_manager_config config;
69715
69716 MA_ZERO_OBJECT(&config);
69718 config.decodedChannels = 0;
69719 config.decodedSampleRate = 0;
69720 config.jobThreadCount = 1; /* A single miniaudio-managed job thread by default. */
69721 config.jobQueueCapacity = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY;
69722
69723 /* Flags. */
69724 config.flags = 0;
69725 #ifdef MA_NO_THREADING
69726 {
69727 /* Threading is disabled at compile time so disable threading at runtime as well by default. */
69729 config.jobThreadCount = 0;
69730 }
69731 #endif
69732
69733 return config;
69734}
69735
69736
69737MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager)
69738{
69739 ma_result result;
69740 ma_job_queue_config jobQueueConfig;
69741
69742 if (pResourceManager == NULL) {
69743 return MA_INVALID_ARGS;
69744 }
69745
69746 MA_ZERO_OBJECT(pResourceManager);
69747
69748 if (pConfig == NULL) {
69749 return MA_INVALID_ARGS;
69750 }
69751
69752 #ifndef MA_NO_THREADING
69753 {
69754 if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) {
69755 return MA_INVALID_ARGS; /* Requesting too many job threads. */
69756 }
69757 }
69758 #endif
69759
69760 pResourceManager->config = *pConfig;
69761 ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks);
69762
69763 /* Get the log set up early so we can start using it as soon as possible. */
69764 if (pResourceManager->config.pLog == NULL) {
69765 result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log);
69766 if (result == MA_SUCCESS) {
69767 pResourceManager->config.pLog = &pResourceManager->log;
69768 } else {
69769 pResourceManager->config.pLog = NULL; /* Logging is unavailable. */
69770 }
69771 }
69772
69773 if (pResourceManager->config.pVFS == NULL) {
69774 result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks);
69775 if (result != MA_SUCCESS) {
69776 return result; /* Failed to initialize the default file system. */
69777 }
69778
69779 pResourceManager->config.pVFS = &pResourceManager->defaultVFS;
69780 }
69781
69782 /* If threading has been disabled at compile time, enforce it at run time as well. */
69783 #ifdef MA_NO_THREADING
69784 {
69786 }
69787 #endif
69788
69789 /* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */
69790 if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) {
69792
69793 /* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */
69794 if (pResourceManager->config.jobThreadCount > 0) {
69795 return MA_INVALID_ARGS;
69796 }
69797 }
69798
69799 /* Job queue. */
69800 jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity;
69801 jobQueueConfig.flags = 0;
69802 if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) {
69803 if (pResourceManager->config.jobThreadCount > 0) {
69804 return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */
69805 }
69806
69807 jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING;
69808 }
69809
69810 result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue);
69811 if (result != MA_SUCCESS) {
69812 return result;
69813 }
69814
69815
69816 /* Custom decoding backends. */
69817 if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) {
69818 size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount;
69819 ma_decoding_backend_vtable** ppCustomDecodingBackendVTables;
69820
69821 ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks);
69822 if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) {
69823 ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
69824 return MA_OUT_OF_MEMORY;
69825 }
69826
69827 MA_COPY_MEMORY(ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes);
69828
69829 pResourceManager->config.ppCustomDecodingBackendVTables = ppCustomDecodingBackendVTables;
69832 }
69833
69834
69835
69836 /* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */
69837 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
69838 #ifndef MA_NO_THREADING
69839 {
69840 ma_uint32 iJobThread;
69841
69842 /* Data buffer lock. */
69843 result = ma_mutex_init(&pResourceManager->dataBufferBSTLock);
69844 if (result != MA_SUCCESS) {
69845 ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
69846 return result;
69847 }
69848
69849 /* Create the job threads last to ensure the threads has access to valid data. */
69850 for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {
69851 result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, pResourceManager->config.jobThreadStackSize, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks);
69852 if (result != MA_SUCCESS) {
69853 ma_mutex_uninit(&pResourceManager->dataBufferBSTLock);
69854 ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
69855 return result;
69856 }
69857 }
69858 }
69859 #else
69860 {
69861 /* Threading is disabled at compile time. We should never get here because validation checks should have already been performed. */
69862 MA_ASSERT(MA_FALSE);
69863 }
69864 #endif
69865 }
69866
69867 return MA_SUCCESS;
69868}
69869
69870
69871static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager)
69872{
69873 MA_ASSERT(pResourceManager);
69874
69875 /* If everything was done properly, there shouldn't be any active data buffers. */
69876 while (pResourceManager->pRootDataBufferNode != NULL) {
69877 ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode;
69878 ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
69879
69880 /* The data buffer has been removed from the BST, so now we need to free its data. */
69881 ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
69882 }
69883}
69884
69886{
69887 if (pResourceManager == NULL) {
69888 return;
69889 }
69890
69891 /*
69892 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
69893 queue which means it will never not be returned after being encountered for the first time which means all threads will eventually receive it.
69894 */
69895 ma_resource_manager_post_job_quit(pResourceManager);
69896
69897 /* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */
69898 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
69899 #ifndef MA_NO_THREADING
69900 {
69901 ma_uint32 iJobThread;
69902
69903 for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {
69904 ma_thread_wait(&pResourceManager->jobThreads[iJobThread]);
69905 }
69906 }
69907 #else
69908 {
69909 MA_ASSERT(MA_FALSE); /* Should never hit this. */
69910 }
69911 #endif
69912 }
69913
69914 /* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */
69915 ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager);
69916
69917 /* The job queue is no longer needed. */
69918 ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
69919
69920 /* We're no longer doing anything with data buffers so the lock can now be uninitialized. */
69921 if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
69922 #ifndef MA_NO_THREADING
69923 {
69924 ma_mutex_uninit(&pResourceManager->dataBufferBSTLock);
69925 }
69926 #else
69927 {
69928 MA_ASSERT(MA_FALSE); /* Should never hit this. */
69929 }
69930 #endif
69931 }
69932
69933 ma_free((ma_decoding_backend_vtable**)pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); /* <-- Naughty const-cast, but this is safe. */
69934
69935 if (pResourceManager->config.pLog == &pResourceManager->log) {
69936 ma_log_uninit(&pResourceManager->log);
69937 }
69938}
69939
69941{
69942 if (pResourceManager == NULL) {
69943 return NULL;
69944 }
69945
69946 return pResourceManager->config.pLog;
69947}
69948
69949
69950
69951MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void)
69952{
69953 ma_resource_manager_data_source_config config;
69954
69955 MA_ZERO_OBJECT(&config);
69956 config.rangeBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG;
69957 config.rangeEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END;
69958 config.loopPointBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG;
69959 config.loopPointEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END;
69960 config.isLooping = MA_FALSE;
69961
69962 return config;
69963}
69964
69965
69966static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager)
69967{
69968 ma_decoder_config config;
69969
69970 config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate);
69971 config.allocationCallbacks = pResourceManager->config.allocationCallbacks;
69973 config.customBackendCount = pResourceManager->config.customDecodingBackendCount;
69975
69976 return config;
69977}
69978
69979static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder)
69980{
69981 ma_result result;
69982 ma_decoder_config config;
69983
69984 MA_ASSERT(pResourceManager != NULL);
69985 MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
69986 MA_ASSERT(pDecoder != NULL);
69987
69988 config = ma_resource_manager__init_decoder_config(pResourceManager);
69989
69990 if (pFilePath != NULL) {
69991 result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder);
69992 if (result != MA_SUCCESS) {
69993 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));
69994 return result;
69995 }
69996 } else {
69997 result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder);
69998 if (result != MA_SUCCESS) {
69999 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
70000 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));
70001 #endif
70002 return result;
70003 }
70004 }
70005
70006 return MA_SUCCESS;
70007}
70008
70009static ma_bool32 ma_resource_manager_data_buffer_has_connector(ma_resource_manager_data_buffer* pDataBuffer)
70010{
70011 return ma_atomic_bool32_get(&pDataBuffer->isConnectorInitialized);
70012}
70013
70014static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer)
70015{
70016 if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) {
70017 return NULL; /* Connector not yet initialized. */
70018 }
70019
70020 switch (pDataBuffer->pNode->data.type)
70021 {
70025
70027 default:
70028 {
70029 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");
70030 return NULL;
70031 };
70032 };
70033}
70034
70035static 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)
70036{
70037 ma_result result;
70038
70039 MA_ASSERT(pDataBuffer != NULL);
70040 MA_ASSERT(pConfig != NULL);
70041 MA_ASSERT(ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE);
70042
70043 /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */
70044 result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode);
70045 if (result != MA_SUCCESS && result != MA_BUSY) {
70046 return result; /* The data buffer is in an erroneous state. */
70047 }
70048
70049 /*
70050 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
70051 "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
70052 an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead.
70053 */
70054 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
70055 {
70056 case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */
70057 {
70058 ma_decoder_config config;
70059 config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager);
70060 result = ma_decoder_init_memory(pDataBuffer->pNode->data.backend.encoded.pData, pDataBuffer->pNode->data.backend.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder);
70061 } break;
70062
70063 case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */
70064 {
70065 ma_audio_buffer_config config;
70067 result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer);
70068 } break;
70069
70070 case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */
70071 {
70072 ma_paged_audio_buffer_config config;
70074 result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer);
70075 } break;
70076
70078 default:
70079 {
70080 /* Unknown data supply type. Should never happen. Need to post an error here. */
70081 return MA_INVALID_ARGS;
70082 };
70083 }
70084
70085 /*
70086 Initialization of the connector is when we can fire the init notification. This will give the application access to
70087 the format/channels/rate of the data source.
70088 */
70089 if (result == MA_SUCCESS) {
70090 /*
70091 The resource manager supports the ability to set the range and loop settings via a config at
70092 initialization time. This results in an case where the ranges could be set explicitly via
70093 ma_data_source_set_*() before we get to this point here. If this happens, we'll end up
70094 hitting a case where we just override those settings which results in what feels like a bug.
70095
70096 To address this we only change the relevant properties if they're not equal to defaults. If
70097 they're equal to defaults there's no need to change them anyway. If they're *not* set to the
70098 default values, we can assume the user has set the range and loop settings via the config. If
70099 they're doing their own calls to ma_data_source_set_*() in addition to setting them via the
70100 config, that's entirely on the caller and any synchronization issue becomes their problem.
70101 */
70102 if (pConfig->rangeBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_BEG || pConfig->rangeEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_END) {
70104 }
70105
70106 if (pConfig->loopPointBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG || pConfig->loopPointEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END) {
70108 }
70109
70110 if (pConfig->isLooping != MA_FALSE) {
70111 ma_data_source_set_looping(pDataBuffer, pConfig->isLooping);
70112 }
70113
70114 ma_atomic_bool32_set(&pDataBuffer->isConnectorInitialized, MA_TRUE);
70115
70116 if (pInitNotification != NULL) {
70117 ma_async_notification_signal(pInitNotification);
70118 }
70119
70120 if (pInitFence != NULL) {
70121 ma_fence_release(pInitFence);
70122 }
70123 }
70124
70125 /* 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. */
70126 return result;
70127}
70128
70129static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer)
70130{
70131 MA_ASSERT(pResourceManager != NULL);
70132 MA_ASSERT(pDataBuffer != NULL);
70133
70134 (void)pResourceManager;
70135
70136 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
70137 {
70138 case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */
70139 {
70140 ma_decoder_uninit(&pDataBuffer->connector.decoder);
70141 } break;
70142
70143 case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */
70144 {
70146 } break;
70147
70148 case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */
70149 {
70151 } break;
70152
70154 default:
70155 {
70156 /* Unknown data supply type. Should never happen. Need to post an error here. */
70157 return MA_INVALID_ARGS;
70158 };
70159 }
70160
70161 return MA_SUCCESS;
70162}
70163
70164static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode)
70165{
70166 MA_ASSERT(pDataBufferNode != NULL);
70167 return ma_atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1);
70168}
70169
70170static 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)
70171{
70172 ma_result result;
70173 size_t dataSizeInBytes;
70174 void* pData;
70175
70176 MA_ASSERT(pResourceManager != NULL);
70177 MA_ASSERT(pDataBufferNode != NULL);
70178 MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
70179
70180 result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks);
70181 if (result != MA_SUCCESS) {
70182 if (pFilePath != NULL) {
70183 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));
70184 } else {
70185 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
70186 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));
70187 #endif
70188 }
70189
70190 return result;
70191 }
70192
70193 pDataBufferNode->data.backend.encoded.pData = pData;
70194 pDataBufferNode->data.backend.encoded.sizeInBytes = dataSizeInBytes;
70195 ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded); /* <-- Must be set last. */
70196
70197 return MA_SUCCESS;
70198}
70199
70200static 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)
70201{
70202 ma_result result = MA_SUCCESS;
70203 ma_decoder* pDecoder;
70204 ma_uint64 totalFrameCount;
70205
70206 MA_ASSERT(pResourceManager != NULL);
70207 MA_ASSERT(pDataBufferNode != NULL);
70208 MA_ASSERT(ppDecoder != NULL);
70209 MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
70210
70211 *ppDecoder = NULL; /* For safety. */
70212
70213 pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks);
70214 if (pDecoder == NULL) {
70215 return MA_OUT_OF_MEMORY;
70216 }
70217
70218 result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder);
70219 if (result != MA_SUCCESS) {
70220 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
70221 return result;
70222 }
70223
70224 /*
70225 At this point we have the decoder and we now need to initialize the data supply. This will
70226 be either a decoded buffer, or a decoded paged buffer. A regular buffer is just one big heap
70227 allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter
70228 is used when the length of a sound is unknown until a full decode has been performed.
70229 */
70231 result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount);
70232 if (result != MA_SUCCESS) {
70233 return result;
70234 }
70235 } else {
70236 totalFrameCount = 0;
70237 }
70238
70239 if (totalFrameCount > 0) {
70240 /* It's a known length. The data supply is a regular decoded buffer. */
70241 ma_uint64 dataSizeInBytes;
70242 void* pData;
70243
70244 dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
70245 if (dataSizeInBytes > MA_SIZE_MAX) {
70246 ma_decoder_uninit(pDecoder);
70247 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
70248 return MA_TOO_BIG;
70249 }
70250
70251 pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks);
70252 if (pData == NULL) {
70253 ma_decoder_uninit(pDecoder);
70254 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
70255 return MA_OUT_OF_MEMORY;
70256 }
70257
70258 /* The buffer needs to be initialized to silence in case the caller reads from it. */
70259 ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels);
70260
70261 /* Data has been allocated and the data supply can now be initialized. */
70262 pDataBufferNode->data.backend.decoded.pData = pData;
70263 pDataBufferNode->data.backend.decoded.totalFrameCount = totalFrameCount;
70264 pDataBufferNode->data.backend.decoded.format = pDecoder->outputFormat;
70265 pDataBufferNode->data.backend.decoded.channels = pDecoder->outputChannels;
70266 pDataBufferNode->data.backend.decoded.sampleRate = pDecoder->outputSampleRate;
70267 pDataBufferNode->data.backend.decoded.decodedFrameCount = 0;
70268 ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded); /* <-- Must be set last. */
70269 } else {
70270 /*
70271 It's an unknown length. The data supply is a paged decoded buffer. Setting this up is
70272 actually easier than the non-paged decoded buffer because we just need to initialize
70273 a ma_paged_audio_buffer object.
70274 */
70275 result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.backend.decodedPaged.data);
70276 if (result != MA_SUCCESS) {
70277 ma_decoder_uninit(pDecoder);
70278 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
70279 return result;
70280 }
70281
70282 pDataBufferNode->data.backend.decodedPaged.sampleRate = pDecoder->outputSampleRate;
70283 pDataBufferNode->data.backend.decodedPaged.decodedFrameCount = 0;
70284 ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged); /* <-- Must be set last. */
70285 }
70286
70287 *ppDecoder = pDecoder;
70288
70289 return MA_SUCCESS;
70290}
70291
70292static 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)
70293{
70294 ma_result result = MA_SUCCESS;
70295 ma_uint64 pageSizeInFrames;
70296 ma_uint64 framesToTryReading;
70297 ma_uint64 framesRead;
70298
70299 MA_ASSERT(pResourceManager != NULL);
70300 MA_ASSERT(pDataBufferNode != NULL);
70301 MA_ASSERT(pDecoder != NULL);
70302
70303 /* We need to know the size of a page in frames to know how many frames to decode. */
70304 pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000);
70305 framesToTryReading = pageSizeInFrames;
70306
70307 /*
70308 Here is where we do the decoding of the next page. We'll run a slightly different path depending
70309 on whether or not we're using a flat or paged buffer because the allocation of the page differs
70310 between the two. For a flat buffer it's an offset to an already-allocated buffer. For a paged
70311 buffer, we need to allocate a new page and attach it to the linked list.
70312 */
70313 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode))
70314 {
70316 {
70317 /* The destination buffer is an offset to the existing buffer. Don't read more than we originally retrieved when we first initialized the decoder. */
70318 void* pDst;
70319 ma_uint64 framesRemaining = pDataBufferNode->data.backend.decoded.totalFrameCount - pDataBufferNode->data.backend.decoded.decodedFrameCount;
70320 if (framesToTryReading > framesRemaining) {
70321 framesToTryReading = framesRemaining;
70322 }
70323
70324 if (framesToTryReading > 0) {
70325 pDst = ma_offset_ptr(
70326 pDataBufferNode->data.backend.decoded.pData,
70327 pDataBufferNode->data.backend.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.backend.decoded.format, pDataBufferNode->data.backend.decoded.channels)
70328 );
70329 MA_ASSERT(pDst != NULL);
70330
70331 result = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading, &framesRead);
70332 if (framesRead > 0) {
70333 pDataBufferNode->data.backend.decoded.decodedFrameCount += framesRead;
70334 }
70335 } else {
70336 framesRead = 0;
70337 }
70338 } break;
70339
70341 {
70342 /* The destination buffer is a freshly allocated page. */
70344
70345 result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.backend.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage);
70346 if (result != MA_SUCCESS) {
70347 return result;
70348 }
70349
70350 result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead);
70351 if (result == MA_SUCCESS && framesRead > 0) {
70352 pPage->sizeInFrames = framesRead;
70353
70354 result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage);
70355 if (result == MA_SUCCESS) {
70356 pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead;
70357 } else {
70358 /* Failed to append the page. Just abort and set the status to MA_AT_END. */
70359 ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks);
70360 result = MA_AT_END;
70361 }
70362 } else {
70363 /* No frames were read. Free the page and just set the status to MA_AT_END. */
70364 ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks);
70365 result = MA_AT_END;
70366 }
70367 } break;
70368
70371 default:
70372 {
70373 /* Unexpected data supply type. */
70374 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));
70375 return MA_ERROR;
70376 };
70377 }
70378
70379 if (result == MA_SUCCESS && framesRead == 0) {
70380 result = MA_AT_END;
70381 }
70382
70383 return result;
70384}
70385
70386static 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)
70387{
70388 ma_result result = MA_SUCCESS;
70389 ma_resource_manager_data_buffer_node* pDataBufferNode = NULL;
70391
70392 if (ppDataBufferNode != NULL) {
70393 *ppDataBufferNode = NULL;
70394 }
70395
70396 result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint);
70397 if (result == MA_ALREADY_EXISTS) {
70398 /* The node already exists. We just need to increment the reference count. */
70399 pDataBufferNode = pInsertPoint;
70400
70401 result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL);
70402 if (result != MA_SUCCESS) {
70403 return result; /* Should never happen. Failed to increment the reference count. */
70404 }
70405
70406 result = MA_ALREADY_EXISTS;
70407 goto done;
70408 } else {
70409 /*
70410 The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This
70411 needs to be done inside the critical section to ensure an uninitialization of the node
70412 does not occur before initialization on another thread.
70413 */
70414 pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks);
70415 if (pDataBufferNode == NULL) {
70416 return MA_OUT_OF_MEMORY;
70417 }
70418
70419 MA_ZERO_OBJECT(pDataBufferNode);
70420 pDataBufferNode->hashedName32 = hashedName32;
70421 pDataBufferNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */
70422
70423 if (pExistingData == NULL) {
70424 pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */
70425 pDataBufferNode->result = MA_BUSY; /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */
70426 pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE;
70427 } else {
70428 pDataBufferNode->data = *pExistingData;
70429 pDataBufferNode->result = MA_SUCCESS; /* Not loading asynchronously, so just set the status */
70430 pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE;
70431 }
70432
70433 result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint);
70434 if (result != MA_SUCCESS) {
70435 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
70436 return result; /* Should never happen. Failed to insert the data buffer into the BST. */
70437 }
70438
70439 /*
70440 Here is where we'll post the job, but only if we're loading asynchronously. If we're
70441 loading synchronously we'll defer loading to a later stage, outside of the critical
70442 section.
70443 */
70444 if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) {
70445 /* Loading asynchronously. Post the job. */
70446 ma_job job;
70447 char* pFilePathCopy = NULL;
70448 wchar_t* pFilePathWCopy = NULL;
70449
70450 /* 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. */
70451 if (pFilePath != NULL) {
70452 pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks);
70453 } else {
70454 pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks);
70455 }
70456
70457 if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {
70458 ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
70459 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
70460 return MA_OUT_OF_MEMORY;
70461 }
70462
70464 ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification);
70465 }
70466
70467 /* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */
70468 if (pInitFence != NULL) { ma_fence_acquire(pInitFence); }
70469 if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); }
70470
70471 /* We now have everything we need to post the job to the job thread. */
70473 job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
70483
70485 result = ma_job_process(&job);
70486 } else {
70487 result = ma_resource_manager_post_job(pResourceManager, &job);
70488 }
70489
70490 if (result != MA_SUCCESS) {
70491 /* Failed to post job. Probably ran out of memory. */
70492 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));
70493
70495 ma_resource_manager_inline_notification_uninit(pInitNotification);
70496 } else {
70497 /*
70498 Fences were acquired before posting the job, but since the job was not able to
70499 be posted, we need to make sure we release them so nothing gets stuck waiting.
70500
70501 In the WAIT_INIT case, these will have already been released in ma_job_process()
70502 so we should only release fences in this branch.
70503 */
70504 if (pInitFence != NULL) { ma_fence_release(pInitFence); }
70505 if (pDoneFence != NULL) { ma_fence_release(pDoneFence); }
70506
70507 /* These will have been freed by the job thread, but with WAIT_INIT they will already have happened since the job has already been handled. */
70508 ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks);
70509 ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks);
70510 }
70511
70512 ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
70513 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
70514
70515 return result;
70516 }
70517 }
70518 }
70519
70520done:
70521 if (ppDataBufferNode != NULL) {
70522 *ppDataBufferNode = pDataBufferNode;
70523 }
70524
70525 return result;
70526}
70527
70528static 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)
70529{
70530 ma_result result = MA_SUCCESS;
70531 ma_bool32 nodeAlreadyExists = MA_FALSE;
70532 ma_resource_manager_data_buffer_node* pDataBufferNode = NULL;
70533 ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */
70534
70535 if (ppDataBufferNode != NULL) {
70536 *ppDataBufferNode = NULL; /* Safety. */
70537 }
70538
70539 if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) {
70540 return MA_INVALID_ARGS;
70541 }
70542
70543 /* If we're specifying existing data, it must be valid. */
70544 if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) {
70545 return MA_INVALID_ARGS;
70546 }
70547
70548 /* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */
70549 if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
70550 flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC;
70551 }
70552
70553 if (hashedName32 == 0) {
70554 if (pFilePath != NULL) {
70555 hashedName32 = ma_hash_string_32(pFilePath);
70556 } else {
70557 hashedName32 = ma_hash_string_w_32(pFilePathW);
70558 }
70559 }
70560
70561 /*
70562 Here is where we either increment the node's reference count or allocate a new one and add it
70563 to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is
70564 posted inside the critical section just in case the caller immediately uninitializes the node
70565 as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the
70566 node is not uninitialized before initialization.
70567 */
70568 ma_resource_manager_data_buffer_bst_lock(pResourceManager);
70569 {
70570 result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode);
70571 }
70572 ma_resource_manager_data_buffer_bst_unlock(pResourceManager);
70573
70574 if (result == MA_ALREADY_EXISTS) {
70575 nodeAlreadyExists = MA_TRUE;
70576 result = MA_SUCCESS;
70577 } else {
70578 if (result != MA_SUCCESS) {
70579 return result;
70580 }
70581 }
70582
70583 /*
70584 If we're loading synchronously, we'll need to load everything now. When loading asynchronously,
70585 a job will have been posted inside the BST critical section so that an uninitialization can be
70586 allocated an appropriate execution order thereby preventing it from being uninitialized before
70587 the node is initialized by the decoding thread(s).
70588 */
70589 if (nodeAlreadyExists == MA_FALSE) { /* Don't need to try loading anything if the node already exists. */
70590 if (pFilePath == NULL && pFilePathW == NULL) {
70591 /*
70592 If this path is hit, it means a buffer is being copied (i.e. initialized from only the
70593 hashed name), but that node has been freed in the meantime, probably from some other
70594 thread. This is an invalid operation.
70595 */
70596 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");
70597 result = MA_INVALID_OPERATION;
70598 goto done;
70599 }
70600
70601 if (pDataBufferNode->isDataOwnedByResourceManager) {
70602 if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) {
70603 /* Loading synchronously. Load the sound in it's entirety here. */
70605 /* No decoding. This is the simple case - just store the file contents in memory. */
70606 result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW);
70607 if (result != MA_SUCCESS) {
70608 goto done;
70609 }
70610 } else {
70611 /* Decoding. We do this the same way as we do when loading asynchronously. */
70612 ma_decoder* pDecoder;
70613 result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder);
70614 if (result != MA_SUCCESS) {
70615 goto done;
70616 }
70617
70618 /* We have the decoder, now decode page by page just like we do when loading asynchronously. */
70619 for (;;) {
70620 /* Decode next page. */
70621 result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder);
70622 if (result != MA_SUCCESS) {
70623 break; /* Will return MA_AT_END when the last page has been decoded. */
70624 }
70625 }
70626
70627 /* Reaching the end needs to be considered successful. */
70628 if (result == MA_AT_END) {
70629 result = MA_SUCCESS;
70630 }
70631
70632 /*
70633 At this point the data buffer is either fully decoded or some error occurred. Either
70634 way, the decoder is no longer necessary.
70635 */
70636 ma_decoder_uninit(pDecoder);
70637 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
70638 }
70639
70640 /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */
70641 ma_atomic_exchange_i32(&pDataBufferNode->result, result);
70642 } else {
70643 /* Loading asynchronously. We may need to wait for initialization. */
70645 ma_resource_manager_inline_notification_wait(&initNotification);
70646 }
70647 }
70648 } else {
70649 /* The data is not managed by the resource manager so there's nothing else to do. */
70650 MA_ASSERT(pExistingData != NULL);
70651 }
70652 }
70653
70654done:
70655 /* If we failed to initialize the data buffer we need to free it. */
70656 if (result != MA_SUCCESS) {
70657 if (nodeAlreadyExists == MA_FALSE) {
70658 ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
70659 ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
70660 }
70661 }
70662
70663 /*
70664 The init notification needs to be uninitialized. This will be used if the node does not already
70665 exist, and we've specified ASYNC | WAIT_INIT.
70666 */
70667 if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) {
70669 ma_resource_manager_inline_notification_uninit(&initNotification);
70670 }
70671 }
70672
70673 if (ppDataBufferNode != NULL) {
70674 *ppDataBufferNode = pDataBufferNode;
70675 }
70676
70677 return result;
70678}
70679
70680static 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)
70681{
70682 ma_result result = MA_SUCCESS;
70683 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. */
70684 ma_uint32 hashedName32 = 0;
70685
70686 if (pResourceManager == NULL) {
70687 return MA_INVALID_ARGS;
70688 }
70689
70690 if (pDataBufferNode == NULL) {
70691 if (pName == NULL && pNameW == NULL) {
70692 return MA_INVALID_ARGS;
70693 }
70694
70695 if (pName != NULL) {
70696 hashedName32 = ma_hash_string_32(pName);
70697 } else {
70698 hashedName32 = ma_hash_string_w_32(pNameW);
70699 }
70700 }
70701
70702 /*
70703 The first thing to do is decrement the reference counter of the node. Then, if the reference
70704 count is zero, we need to free the node. If the node is still in the process of loading, we'll
70705 need to post a job to the job queue to free the node. Otherwise we'll just do it here.
70706 */
70707 ma_resource_manager_data_buffer_bst_lock(pResourceManager);
70708 {
70709 /* Might need to find the node. Must be done inside the critical section. */
70710 if (pDataBufferNode == NULL) {
70711 result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode);
70712 if (result != MA_SUCCESS) {
70713 goto stage2; /* Couldn't find the node. */
70714 }
70715 }
70716
70717 result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount);
70718 if (result != MA_SUCCESS) {
70719 goto stage2; /* Should never happen. */
70720 }
70721
70722 if (refCount == 0) {
70723 result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
70724 if (result != MA_SUCCESS) {
70725 goto stage2; /* An error occurred when trying to remove the data buffer. This should never happen. */
70726 }
70727 }
70728 }
70729 ma_resource_manager_data_buffer_bst_unlock(pResourceManager);
70730
70731stage2:
70732 if (result != MA_SUCCESS) {
70733 return result;
70734 }
70735
70736 /*
70737 Here is where we need to free the node. We don't want to do this inside the critical section
70738 above because we want to keep that as small as possible for multi-threaded efficiency.
70739 */
70740 if (refCount == 0) {
70741 if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) {
70742 /* The sound is still loading. We need to delay the freeing of the node to a safe time. */
70743 ma_job job;
70744
70745 /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */
70746 ma_atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE);
70747
70749 job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
70752
70753 result = ma_resource_manager_post_job(pResourceManager, &job);
70754 if (result != MA_SUCCESS) {
70755 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));
70756 return result;
70757 }
70758
70759 /* If we don't support threading, process the job queue here. */
70760 if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
70761 while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) {
70762 result = ma_resource_manager_process_next_job(pResourceManager);
70763 if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) {
70764 result = MA_SUCCESS;
70765 break;
70766 }
70767 }
70768 } else {
70769 /* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */
70770 }
70771 } else {
70772 /* The sound isn't loading so we can just free the node here. */
70773 ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
70774 }
70775 }
70776
70777 return result;
70778}
70779
70780
70781
70782static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer)
70783{
70784 MA_ASSERT(pDataBuffer != NULL);
70785 return ma_atomic_fetch_add_32(&pDataBuffer->executionCounter, 1);
70786}
70787
70788static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
70789{
70790 return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead);
70791}
70792
70793static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
70794{
70796}
70797
70798static 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)
70799{
70800 return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
70801}
70802
70803static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
70804{
70806}
70807
70808static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
70809{
70811}
70812
70813static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
70814{
70816 MA_ASSERT(pDataBuffer != NULL);
70817
70818 ma_atomic_exchange_32(&pDataBuffer->isLooping, isLooping);
70819
70820 /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */
70821 ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping);
70822
70823 return MA_SUCCESS;
70824}
70825
70826static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable =
70827{
70828 ma_resource_manager_data_buffer_cb__read_pcm_frames,
70829 ma_resource_manager_data_buffer_cb__seek_to_pcm_frame,
70830 ma_resource_manager_data_buffer_cb__get_data_format,
70831 ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames,
70832 ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames,
70833 ma_resource_manager_data_buffer_cb__set_looping,
70834 0
70835};
70836
70837static 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)
70838{
70839 ma_result result = MA_SUCCESS;
70840 ma_resource_manager_data_buffer_node* pDataBufferNode;
70841 ma_data_source_config dataSourceConfig;
70842 ma_bool32 async;
70843 ma_uint32 flags;
70844 ma_resource_manager_pipeline_notifications notifications;
70845
70846 if (pDataBuffer == NULL) {
70847 if (pConfig != NULL && pConfig->pNotifications != NULL) {
70848 ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications);
70849 }
70850
70851 return MA_INVALID_ARGS;
70852 }
70853
70854 MA_ZERO_OBJECT(pDataBuffer);
70855
70856 if (pConfig == NULL) {
70857 return MA_INVALID_ARGS;
70858 }
70859
70860 if (pConfig->pNotifications != NULL) {
70861 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. */
70862 } else {
70863 MA_ZERO_OBJECT(&notifications);
70864 }
70865
70866 /* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */
70867 flags = pConfig->flags;
70868 if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
70869 flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC;
70870 }
70871
70872 if (pConfig->isLooping) {
70874 }
70875
70876 async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0;
70877
70878 /*
70879 Fences need to be acquired before doing anything. These must be acquired and released outside of
70880 the node to ensure there's no holes where ma_fence_wait() could prematurely return before the
70881 data buffer has completed initialization.
70882
70883 When loading asynchronously, the node acquisition routine below will acquire the fences on this
70884 thread and then release them on the async thread when the operation is complete.
70885
70886 These fences are always released at the "done" tag at the end of this function. They'll be
70887 acquired a second if loading asynchronously. This double acquisition system is just done to
70888 simplify code maintenance.
70889 */
70890 ma_resource_manager_pipeline_notifications_acquire_all_fences(&notifications);
70891 {
70892 /* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */
70893 result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode);
70894 if (result != MA_SUCCESS) {
70895 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
70896 goto done;
70897 }
70898
70899 dataSourceConfig = ma_data_source_config_init();
70900 dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable;
70901
70902 result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds);
70903 if (result != MA_SUCCESS) {
70904 ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
70905 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
70906 goto done;
70907 }
70908
70909 pDataBuffer->pResourceManager = pResourceManager;
70910 pDataBuffer->pNode = pDataBufferNode;
70911 pDataBuffer->flags = flags;
70912 pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */
70913
70914 /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */
70915 if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) {
70916 /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */
70917 result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL);
70918 ma_atomic_exchange_i32(&pDataBuffer->result, result);
70919
70920 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
70921 goto done;
70922 } else {
70923 /* 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. */
70924 ma_job job;
70925 ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */
70926
70928 ma_resource_manager_inline_notification_init(pResourceManager, &initNotification);
70929 }
70930
70931 /*
70932 The status of the data buffer needs to be set to MA_BUSY before posting the job so that the
70933 worker thread is aware of its busy state. If the LOAD_DATA_BUFFER job sees a status other
70934 than MA_BUSY, it'll assume an error and fall through to an early exit.
70935 */
70936 ma_atomic_exchange_i32(&pDataBuffer->result, MA_BUSY);
70937
70938 /* Acquire fences a second time. These will be released by the async thread. */
70939 ma_resource_manager_pipeline_notifications_acquire_all_fences(&notifications);
70940
70942 job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
70944 job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification;
70953
70954 /* If we need to wait for initialization to complete we can just process the job in place. */
70956 result = ma_job_process(&job);
70957 } else {
70958 result = ma_resource_manager_post_job(pResourceManager, &job);
70959 }
70960
70961 if (result != MA_SUCCESS) {
70962 /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */
70963 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));
70964 ma_atomic_exchange_i32(&pDataBuffer->result, result);
70965
70966 /* Release the fences after the result has been set on the data buffer. */
70967 ma_resource_manager_pipeline_notifications_release_all_fences(&notifications);
70968 } else {
70970 ma_resource_manager_inline_notification_wait(&initNotification);
70971
70972 if (notifications.init.pNotification != NULL) {
70974 }
70975
70976 /* NOTE: Do not release the init fence here. It will have been done by the job. */
70977
70978 /* Make sure we return an error if initialization failed on the async thread. */
70979 result = ma_resource_manager_data_buffer_result(pDataBuffer);
70980 if (result == MA_BUSY) {
70981 result = MA_SUCCESS;
70982 }
70983 }
70984 }
70985
70987 ma_resource_manager_inline_notification_uninit(&initNotification);
70988 }
70989 }
70990
70991 if (result != MA_SUCCESS) {
70992 ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
70993 goto done;
70994 }
70995 }
70996done:
70997 if (result == MA_SUCCESS) {
70998 if (pConfig->initialSeekPointInPCMFrames > 0) {
71000 }
71001 }
71002
71003 ma_resource_manager_pipeline_notifications_release_all_fences(&notifications);
71004
71005 return result;
71006}
71007
71008MA_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)
71009{
71010 return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer);
71011}
71012
71013MA_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)
71014{
71015 ma_resource_manager_data_source_config config;
71016
71018 config.pFilePath = pFilePath;
71019 config.flags = flags;
71020 config.pNotifications = pNotifications;
71021
71022 return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer);
71023}
71024
71025MA_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)
71026{
71027 ma_resource_manager_data_source_config config;
71028
71030 config.pFilePathW = pFilePath;
71031 config.flags = flags;
71032 config.pNotifications = pNotifications;
71033
71034 return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer);
71035}
71036
71038{
71039 ma_resource_manager_data_source_config config;
71040
71041 if (pExistingDataBuffer == NULL) {
71042 return MA_INVALID_ARGS;
71043 }
71044
71045 MA_ASSERT(pExistingDataBuffer->pNode != NULL); /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */
71046
71048 config.flags = pExistingDataBuffer->flags;
71049
71050 return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer);
71051}
71052
71053static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer)
71054{
71055 MA_ASSERT(pDataBuffer != NULL);
71056
71057 /* The connector should be uninitialized first. */
71058 ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer);
71059
71060 /* With the connector uninitialized we can unacquire the node. */
71061 ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL);
71062
71063 /* The base data source needs to be uninitialized as well. */
71064 ma_data_source_uninit(&pDataBuffer->ds);
71065
71066 return MA_SUCCESS;
71067}
71068
71070{
71071 ma_result result;
71072
71073 if (pDataBuffer == NULL) {
71074 return MA_INVALID_ARGS;
71075 }
71076
71078 /* The data buffer can be deleted synchronously. */
71079 return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
71080 } else {
71081 /*
71082 The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will
71083 be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event
71084 to get processed before returning.
71085 */
71086 ma_resource_manager_inline_notification notification;
71087 ma_job job;
71088
71089 /*
71090 We need to mark the node as unavailable so we don't try reading from it anymore, but also to
71091 let the loading thread know that it needs to abort it's loading procedure.
71092 */
71093 ma_atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE);
71094
71095 result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, &notification);
71096 if (result != MA_SUCCESS) {
71097 return result; /* Failed to create the notification. This should rarely, if ever, happen. */
71098 }
71099
71101 job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
71105
71106 result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job);
71107 if (result != MA_SUCCESS) {
71108 ma_resource_manager_inline_notification_uninit(&notification);
71109 return result;
71110 }
71111
71112 ma_resource_manager_inline_notification_wait_and_uninit(&notification);
71113 }
71114
71115 return result;
71116}
71117
71119{
71120 ma_result result = MA_SUCCESS;
71121 ma_uint64 framesRead = 0;
71122 ma_bool32 isDecodedBufferBusy = MA_FALSE;
71123
71124 /* Safety. */
71125 if (pFramesRead != NULL) {
71126 *pFramesRead = 0;
71127 }
71128
71129 if (frameCount == 0) {
71130 return MA_INVALID_ARGS;
71131 }
71132
71133 /*
71134 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
71135 it's been uninitialized or is in the process of uninitializing.
71136 */
71137 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
71138
71139 /* If the node is not initialized we need to abort with a busy code. */
71140 if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) {
71141 return MA_BUSY; /* Still loading. */
71142 }
71143
71144 /*
71145 If we've got a seek scheduled we'll want to do that before reading. However, for paged buffers, there's
71146 a chance that the sound hasn't yet been decoded up to the seek point will result in the seek failing. If
71147 this happens, we need to keep the seek scheduled and return MA_BUSY.
71148 */
71149 if (pDataBuffer->seekToCursorOnNextRead) {
71150 pDataBuffer->seekToCursorOnNextRead = MA_FALSE;
71151
71152 result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames);
71153 if (result != MA_SUCCESS) {
71154 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) {
71155 pDataBuffer->seekToCursorOnNextRead = MA_TRUE; /* Keep the seek scheduled. We just haven't loaded enough data yet to do the seek properly. */
71156 return MA_BUSY;
71157 }
71158
71159 return result;
71160 }
71161 }
71162
71163 /*
71164 For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot
71165 exceed this amount. We'll read as much as we can, and then return MA_BUSY.
71166 */
71167 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) {
71168 ma_uint64 availableFrames;
71169
71170 isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY);
71171
71172 if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) {
71173 /* Don't try reading more than the available frame count if the data buffer node is still loading. */
71174 if (isDecodedBufferBusy) {
71175 if (frameCount > availableFrames) {
71176 frameCount = availableFrames;
71177
71178 /*
71179 If there's no frames available we want to set the status to MA_AT_END. The logic below
71180 will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this
71181 is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count
71182 is 0 because that'll result in a situation where it's possible MA_AT_END won't get
71183 returned.
71184 */
71185 if (frameCount == 0) {
71186 result = MA_AT_END;
71187 }
71188 } else {
71189 isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */
71190 }
71191 } else {
71192 /*
71193 Getting here means the buffer has been fully loaded. We can just pass the frame count straight
71194 into ma_data_source_read_pcm_frames() below and let ma_data_source handle it.
71195 */
71196 }
71197 }
71198 }
71199
71200 /* Don't attempt to read anything if we've got no frames available. */
71201 if (frameCount > 0) {
71202 result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead);
71203 }
71204
71205 /*
71206 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
71207 as at the end and terminate decoding.
71208 */
71209 if (result == MA_AT_END) {
71210 if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) {
71211 result = MA_BUSY;
71212 }
71213 }
71214
71215 if (isDecodedBufferBusy) {
71216 result = MA_BUSY;
71217 }
71218
71219 if (pFramesRead != NULL) {
71220 *pFramesRead = framesRead;
71221 }
71222
71223 if (result == MA_SUCCESS && framesRead == 0) {
71224 result = MA_AT_END;
71225 }
71226
71227 return result;
71228}
71229
71231{
71232 ma_result result;
71233
71234 /* We cannot be using the data source after it's been uninitialized. */
71235 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
71236
71237 /* If we haven't yet got a connector we need to abort. */
71238 if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) {
71239 pDataBuffer->seekTargetInPCMFrames = frameIndex;
71240 pDataBuffer->seekToCursorOnNextRead = MA_TRUE;
71241 return MA_BUSY; /* Still loading. */
71242 }
71243
71244 result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex);
71245 if (result != MA_SUCCESS) {
71246 return result;
71247 }
71248
71249 pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0; /* <-- For identification purposes. */
71250 pDataBuffer->seekToCursorOnNextRead = MA_FALSE;
71251
71252 return MA_SUCCESS;
71253}
71254
71255MA_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)
71256{
71257 /* We cannot be using the data source after it's been uninitialized. */
71258 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
71259
71260 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
71261 {
71263 {
71264 return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
71265 };
71266
71268 {
71269 *pFormat = pDataBuffer->pNode->data.backend.decoded.format;
71270 *pChannels = pDataBuffer->pNode->data.backend.decoded.channels;
71271 *pSampleRate = pDataBuffer->pNode->data.backend.decoded.sampleRate;
71273 return MA_SUCCESS;
71274 };
71275
71277 {
71278 *pFormat = pDataBuffer->pNode->data.backend.decodedPaged.data.format;
71279 *pChannels = pDataBuffer->pNode->data.backend.decodedPaged.data.channels;
71280 *pSampleRate = pDataBuffer->pNode->data.backend.decodedPaged.sampleRate;
71282 return MA_SUCCESS;
71283 };
71284
71286 {
71287 return MA_BUSY; /* Still loading. */
71288 };
71289
71290 default:
71291 {
71292 /* Unknown supply type. Should never hit this. */
71293 return MA_INVALID_ARGS;
71294 }
71295 }
71296}
71297
71299{
71300 /* We cannot be using the data source after it's been uninitialized. */
71301 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
71302
71303 if (pDataBuffer == NULL || pCursor == NULL) {
71304 return MA_INVALID_ARGS;
71305 }
71306
71307 *pCursor = 0;
71308
71309 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
71310 {
71312 {
71313 return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor);
71314 };
71315
71317 {
71318 return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor);
71319 };
71320
71322 {
71324 };
71325
71327 {
71328 return MA_BUSY;
71329 };
71330
71331 default:
71332 {
71333 return MA_INVALID_ARGS;
71334 }
71335 }
71336}
71337
71339{
71340 /* We cannot be using the data source after it's been uninitialized. */
71341 MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
71342
71343 if (pDataBuffer == NULL || pLength == NULL) {
71344 return MA_INVALID_ARGS;
71345 }
71346
71347 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
71348 return MA_BUSY; /* Still loading. */
71349 }
71350
71351 return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength);
71352}
71353
71355{
71356 if (pDataBuffer == NULL) {
71357 return MA_INVALID_ARGS;
71358 }
71359
71360 return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */
71361}
71362
71364{
71365 return ma_data_source_set_looping(pDataBuffer, isLooping);
71366}
71367
71369{
71370 return ma_data_source_is_looping(pDataBuffer);
71371}
71372
71374{
71375 if (pAvailableFrames == NULL) {
71376 return MA_INVALID_ARGS;
71377 }
71378
71379 *pAvailableFrames = 0;
71380
71381 if (pDataBuffer == NULL) {
71382 return MA_INVALID_ARGS;
71383 }
71384
71385 if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
71386 if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) {
71387 return MA_BUSY;
71388 } else {
71389 return MA_INVALID_OPERATION; /* No connector. */
71390 }
71391 }
71392
71393 switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
71394 {
71396 {
71397 return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames);
71398 };
71399
71401 {
71402 return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames);
71403 };
71404
71406 {
71407 ma_uint64 cursor;
71409
71410 if (pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount > cursor) {
71411 *pAvailableFrames = pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount - cursor;
71412 } else {
71413 *pAvailableFrames = 0;
71414 }
71415
71416 return MA_SUCCESS;
71417 };
71418
71420 default:
71421 {
71422 /* Unknown supply type. Should never hit this. */
71423 return MA_INVALID_ARGS;
71424 }
71425 }
71426}
71427
71428MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags)
71429{
71430 return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL);
71431}
71432
71433MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags)
71434{
71435 return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL);
71436}
71437
71438
71439static ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData)
71440{
71441 return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL);
71442}
71443
71444static 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)
71445{
71446 ma_resource_manager_data_supply data;
71448 data.backend.decoded.pData = pData;
71449 data.backend.decoded.totalFrameCount = frameCount;
71450 data.backend.decoded.format = format;
71451 data.backend.decoded.channels = channels;
71452 data.backend.decoded.sampleRate = sampleRate;
71453
71454 return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data);
71455}
71456
71457MA_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)
71458{
71459 return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate);
71460}
71461
71462MA_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)
71463{
71464 return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate);
71465}
71466
71467
71468static 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)
71469{
71470 ma_resource_manager_data_supply data;
71472 data.backend.encoded.pData = pData;
71473 data.backend.encoded.sizeInBytes = sizeInBytes;
71474
71475 return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data);
71476}
71477
71478MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes)
71479{
71480 return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes);
71481}
71482
71483MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes)
71484{
71485 return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes);
71486}
71487
71488
71489MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath)
71490{
71491 return ma_resource_manager_unregister_data(pResourceManager, pFilePath);
71492}
71493
71494MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath)
71495{
71496 return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath);
71497}
71498
71499MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName)
71500{
71501 return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL);
71502}
71503
71504MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName)
71505{
71506 return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName);
71507}
71508
71509
71510static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream)
71511{
71512 MA_ASSERT(pDataStream != NULL);
71513 return ma_atomic_fetch_add_32(&pDataStream->executionCounter, 1);
71514}
71515
71516static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream)
71517{
71518 MA_ASSERT(pDataStream != NULL);
71519 return ma_atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd);
71520}
71521
71522static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream)
71523{
71524 MA_ASSERT(pDataStream != NULL);
71525 return ma_atomic_load_32((ma_uint32*)&pDataStream->seekCounter);
71526}
71527
71528
71529static ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
71530{
71531 return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead);
71532}
71533
71534static ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
71535{
71537}
71538
71539static 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)
71540{
71541 return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
71542}
71543
71544static ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
71545{
71547}
71548
71549static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
71550{
71552}
71553
71554static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
71555{
71557 MA_ASSERT(pDataStream != NULL);
71558
71559 ma_atomic_exchange_32(&pDataStream->isLooping, isLooping);
71560
71561 return MA_SUCCESS;
71562}
71563
71564static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable =
71565{
71566 ma_resource_manager_data_stream_cb__read_pcm_frames,
71567 ma_resource_manager_data_stream_cb__seek_to_pcm_frame,
71568 ma_resource_manager_data_stream_cb__get_data_format,
71569 ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames,
71570 ma_resource_manager_data_stream_cb__get_length_in_pcm_frames,
71571 ma_resource_manager_data_stream_cb__set_looping,
71572 0 /*MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT*/
71573};
71574
71575static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor)
71576{
71577 /* Loop if possible. */
71578 if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) {
71579 absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames;
71580 }
71581
71582 ma_atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor);
71583}
71584
71585MA_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)
71586{
71587 ma_result result;
71588 ma_data_source_config dataSourceConfig;
71589 char* pFilePathCopy = NULL;
71590 wchar_t* pFilePathWCopy = NULL;
71591 ma_job job;
71592 ma_bool32 waitBeforeReturning = MA_FALSE;
71593 ma_resource_manager_inline_notification waitNotification;
71594 ma_resource_manager_pipeline_notifications notifications;
71595 ma_uint32 flags;
71596
71597 if (pDataStream == NULL) {
71598 if (pConfig != NULL && pConfig->pNotifications != NULL) {
71599 ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications);
71600 }
71601
71602 return MA_INVALID_ARGS;
71603 }
71604
71605 MA_ZERO_OBJECT(pDataStream);
71606
71607 if (pConfig == NULL) {
71608 return MA_INVALID_ARGS;
71609 }
71610
71611 if (pConfig->pNotifications != NULL) {
71612 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. */
71613 } else {
71614 MA_ZERO_OBJECT(&notifications);
71615 }
71616
71617 dataSourceConfig = ma_data_source_config_init();
71618 dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable;
71619
71620 result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds);
71621 if (result != MA_SUCCESS) {
71622 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
71623 return result;
71624 }
71625
71626 flags = pConfig->flags;
71627 if (pConfig->isLooping) {
71629 }
71630
71631 pDataStream->pResourceManager = pResourceManager;
71632 pDataStream->flags = pConfig->flags;
71633 pDataStream->result = MA_BUSY;
71634
71638
71639 if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) {
71640 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
71641 return MA_INVALID_ARGS;
71642 }
71643
71644 /* 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. */
71645
71646 /* 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. */
71647 if (pConfig->pFilePath != NULL) {
71648 pFilePathCopy = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks);
71649 } else {
71650 pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks);
71651 }
71652
71653 if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {
71654 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
71655 return MA_OUT_OF_MEMORY;
71656 }
71657
71658 /*
71659 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
71660 can return immediately. Likewise, we'll also check for MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT and do the same.
71661 */
71663 waitBeforeReturning = MA_TRUE;
71664 ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification);
71665 }
71666
71667 ma_resource_manager_pipeline_notifications_acquire_all_fences(&notifications);
71668
71669 /* Set the absolute cursor to our initial seek position so retrieval of the cursor returns a good value. */
71670 ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames);
71671
71672 /* 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. */
71674 job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
71676 job.data.resourceManager.loadDataStream.pFilePath = pFilePathCopy;
71677 job.data.resourceManager.loadDataStream.pFilePathW = pFilePathWCopy;
71679 job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification;
71681 result = ma_resource_manager_post_job(pResourceManager, &job);
71682 if (result != MA_SUCCESS) {
71683 ma_resource_manager_pipeline_notifications_signal_all_notifications(&notifications);
71684 ma_resource_manager_pipeline_notifications_release_all_fences(&notifications);
71685
71686 if (waitBeforeReturning) {
71687 ma_resource_manager_inline_notification_uninit(&waitNotification);
71688 }
71689
71690 ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks);
71691 ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks);
71692 return result;
71693 }
71694
71695 /* Wait if needed. */
71696 if (waitBeforeReturning) {
71697 ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification);
71698
71699 if (notifications.init.pNotification != NULL) {
71701 }
71702
71703 /*
71704 If there was an error during initialization make sure we return that result here. We don't want to do this
71705 if we're not waiting because it will most likely be in a busy state.
71706 */
71707 if (pDataStream->result != MA_SUCCESS) {
71708 return pDataStream->result;
71709 }
71710
71711 /* NOTE: Do not release pInitFence here. That will be done by the job. */
71712 }
71713
71714 return MA_SUCCESS;
71715}
71716
71717MA_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)
71718{
71719 ma_resource_manager_data_source_config config;
71720
71722 config.pFilePath = pFilePath;
71723 config.flags = flags;
71724 config.pNotifications = pNotifications;
71725
71726 return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream);
71727}
71728
71729MA_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)
71730{
71731 ma_resource_manager_data_source_config config;
71732
71734 config.pFilePathW = pFilePath;
71735 config.flags = flags;
71736 config.pNotifications = pNotifications;
71737
71738 return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream);
71739}
71740
71742{
71743 ma_resource_manager_inline_notification freeEvent;
71744 ma_job job;
71745
71746 if (pDataStream == NULL) {
71747 return MA_INVALID_ARGS;
71748 }
71749
71750 /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */
71751 ma_atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE);
71752
71753 /*
71754 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
71755 to wait for it to complete before returning which means we need an event.
71756 */
71757 ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent);
71758
71760 job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
71765
71766 /* We need to wait for the job to finish processing before we return. */
71767 ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent);
71768
71769 return MA_SUCCESS;
71770}
71771
71772
71773static ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream)
71774{
71775 MA_ASSERT(pDataStream != NULL);
71776 MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE);
71777
71778 return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000);
71779}
71780
71781static void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor)
71782{
71783 MA_ASSERT(pDataStream != NULL);
71784 MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE);
71785 MA_ASSERT(pageIndex == 0 || pageIndex == 1);
71786
71787 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));
71788}
71789
71790static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex)
71791{
71792 ma_result result = MA_SUCCESS;
71793 ma_uint64 pageSizeInFrames;
71794 ma_uint64 totalFramesReadForThisPage = 0;
71795 void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0);
71796
71797 pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream);
71798
71799 /* The decoder needs to inherit the stream's looping and range state. */
71800 {
71801 ma_uint64 rangeBeg;
71802 ma_uint64 rangeEnd;
71803 ma_uint64 loopPointBeg;
71804 ma_uint64 loopPointEnd;
71805
71807
71808 ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd);
71809 ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd);
71810
71811 ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd);
71812 ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd);
71813 }
71814
71815 /* Just read straight from the decoder. It will deal with ranges and looping for us. */
71816 result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage);
71817 if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) {
71818 ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE);
71819 }
71820
71821 ma_atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage);
71822 ma_atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE);
71823}
71824
71825static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream)
71826{
71827 ma_uint32 iPage;
71828
71829 MA_ASSERT(pDataStream != NULL);
71830
71831 for (iPage = 0; iPage < 2; iPage += 1) {
71832 ma_resource_manager_data_stream_fill_page(pDataStream, iPage);
71833 }
71834}
71835
71836
71837static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount)
71838{
71839 ma_uint64 framesAvailable;
71840 ma_uint64 frameCount = 0;
71841
71842 /* We cannot be using the data source after it's been uninitialized. */
71843 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
71844
71845 if (pFrameCount != NULL) {
71846 frameCount = *pFrameCount;
71847 *pFrameCount = 0;
71848 }
71849 if (ppFramesOut != NULL) {
71850 *ppFramesOut = NULL;
71851 }
71852
71853 if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) {
71854 return MA_INVALID_ARGS;
71855 }
71856
71858 return MA_INVALID_OPERATION;
71859 }
71860
71861 /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */
71862 if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {
71863 return MA_BUSY;
71864 }
71865
71866 /* If the page we're on is invalid it means we've caught up to the job thread. */
71867 if (ma_atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) {
71868 framesAvailable = 0;
71869 } else {
71870 /*
71871 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
71872 that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler.
71873 */
71874 ma_uint32 currentPageFrameCount = ma_atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]);
71875 MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor);
71876
71877 framesAvailable = currentPageFrameCount - pDataStream->relativeCursor;
71878 }
71879
71880 /* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */
71881 if (framesAvailable == 0) {
71882 if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) {
71883 return MA_AT_END;
71884 } else {
71885 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. */
71886 }
71887 }
71888
71889 MA_ASSERT(framesAvailable > 0);
71890
71891 if (frameCount > framesAvailable) {
71892 frameCount = framesAvailable;
71893 }
71894
71895 *ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor);
71896 *pFrameCount = frameCount;
71897
71898 return MA_SUCCESS;
71899}
71900
71901static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount)
71902{
71903 ma_uint32 newRelativeCursor;
71904 ma_uint32 pageSizeInFrames;
71905 ma_job job;
71906
71907 /* We cannot be using the data source after it's been uninitialized. */
71908 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
71909
71910 if (pDataStream == NULL) {
71911 return MA_INVALID_ARGS;
71912 }
71913
71915 return MA_INVALID_OPERATION;
71916 }
71917
71918 /* The frame count should always fit inside a 32-bit integer. */
71919 if (frameCount > 0xFFFFFFFF) {
71920 return MA_INVALID_ARGS;
71921 }
71922
71923 pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream);
71924
71925 /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */
71926 ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, ma_atomic_load_64(&pDataStream->absoluteCursor) + frameCount);
71927
71928 /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */
71929 newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount;
71930
71931 /* 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. */
71932 if (newRelativeCursor >= pageSizeInFrames) {
71933 newRelativeCursor -= pageSizeInFrames;
71934
71935 /* Here is where we post the job start decoding. */
71937 job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
71940
71941 /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */
71942 ma_atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE);
71943
71944 /* Before posting the job we need to make sure we set some state. */
71945 pDataStream->relativeCursor = newRelativeCursor;
71946 pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01;
71947 return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
71948 } else {
71949 /* We haven't moved into a new page so we can just move the cursor forward. */
71950 pDataStream->relativeCursor = newRelativeCursor;
71951 return MA_SUCCESS;
71952 }
71953}
71954
71955
71957{
71958 ma_result result = MA_SUCCESS;
71959 ma_uint64 totalFramesProcessed;
71960 ma_format format;
71961 ma_uint32 channels;
71962
71963 /* Safety. */
71964 if (pFramesRead != NULL) {
71965 *pFramesRead = 0;
71966 }
71967
71968 if (frameCount == 0) {
71969 return MA_INVALID_ARGS;
71970 }
71971
71972 /* We cannot be using the data source after it's been uninitialized. */
71973 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
71974
71975 if (pDataStream == NULL) {
71976 return MA_INVALID_ARGS;
71977 }
71978
71980 return MA_INVALID_OPERATION;
71981 }
71982
71983 /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */
71984 if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {
71985 return MA_BUSY;
71986 }
71987
71988 ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0);
71989
71990 /* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */
71991 totalFramesProcessed = 0;
71992 while (totalFramesProcessed < frameCount) {
71993 void* pMappedFrames;
71994 ma_uint64 mappedFrameCount;
71995
71996 mappedFrameCount = frameCount - totalFramesProcessed;
71997 result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount);
71998 if (result != MA_SUCCESS) {
71999 break;
72000 }
72001
72002 /* 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. */
72003 if (pFramesOut != NULL) {
72004 ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels);
72005 }
72006
72007 totalFramesProcessed += mappedFrameCount;
72008
72009 result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount);
72010 if (result != MA_SUCCESS) {
72011 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. */
72012 }
72013 }
72014
72015 if (pFramesRead != NULL) {
72016 *pFramesRead = totalFramesProcessed;
72017 }
72018
72019 if (result == MA_SUCCESS && totalFramesProcessed == 0) {
72020 result = MA_AT_END;
72021 }
72022
72023 return result;
72024}
72025
72027{
72028 ma_job job;
72029 ma_result streamResult;
72030
72031 streamResult = ma_resource_manager_data_stream_result(pDataStream);
72032
72033 /* We cannot be using the data source after it's been uninitialized. */
72034 MA_ASSERT(streamResult != MA_UNAVAILABLE);
72035
72036 if (pDataStream == NULL) {
72037 return MA_INVALID_ARGS;
72038 }
72039
72040 if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) {
72041 return MA_INVALID_OPERATION;
72042 }
72043
72044 /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */
72045 if (ma_atomic_load_32(&pDataStream->seekCounter) == 0) {
72046 if (ma_atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) {
72047 return MA_SUCCESS;
72048 }
72049 }
72050
72051
72052 /* 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. */
72053 ma_atomic_fetch_add_32(&pDataStream->seekCounter, 1);
72054
72055 /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */
72056 ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex);
72057
72058 /*
72059 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
72060 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
72061 the first page.
72062 */
72063 pDataStream->relativeCursor = 0;
72064 pDataStream->currentPageIndex = 0;
72065 ma_atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE);
72066 ma_atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE);
72067
72068 /* 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. */
72069 ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE);
72070
72071 /*
72072 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
72073 are invalid and any content contained within them will be discarded and replaced with newly decoded data.
72074 */
72076 job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
72079 return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
72080}
72081
72082MA_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)
72083{
72084 /* We cannot be using the data source after it's been uninitialized. */
72085 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
72086
72087 if (pFormat != NULL) {
72088 *pFormat = ma_format_unknown;
72089 }
72090
72091 if (pChannels != NULL) {
72092 *pChannels = 0;
72093 }
72094
72095 if (pSampleRate != NULL) {
72096 *pSampleRate = 0;
72097 }
72098
72099 if (pChannelMap != NULL) {
72100 MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
72101 }
72102
72103 if (pDataStream == NULL) {
72104 return MA_INVALID_ARGS;
72105 }
72106
72108 return MA_INVALID_OPERATION;
72109 }
72110
72111 /*
72112 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
72113 such that the application is responsible for ensuring it's not called while uninitializing so it should be safe.
72114 */
72115 return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
72116}
72117
72119{
72120 ma_result result;
72121
72122 if (pCursor == NULL) {
72123 return MA_INVALID_ARGS;
72124 }
72125
72126 *pCursor = 0;
72127
72128 /* We cannot be using the data source after it's been uninitialized. */
72129 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
72130
72131 if (pDataStream == NULL) {
72132 return MA_INVALID_ARGS;
72133 }
72134
72135 /*
72136 If the stream is in an erroneous state we need to return an invalid operation. We can allow
72137 this to be called when the data stream is in a busy state because the caller may have asked
72138 for an initial seek position and it's convenient to return that as the cursor position.
72139 */
72140 result = ma_resource_manager_data_stream_result(pDataStream);
72141 if (result != MA_SUCCESS && result != MA_BUSY) {
72142 return MA_INVALID_OPERATION;
72143 }
72144
72145 *pCursor = ma_atomic_load_64(&pDataStream->absoluteCursor);
72146
72147 return MA_SUCCESS;
72148}
72149
72151{
72152 ma_result streamResult;
72153
72154 if (pLength == NULL) {
72155 return MA_INVALID_ARGS;
72156 }
72157
72158 *pLength = 0;
72159
72160 streamResult = ma_resource_manager_data_stream_result(pDataStream);
72161
72162 /* We cannot be using the data source after it's been uninitialized. */
72163 MA_ASSERT(streamResult != MA_UNAVAILABLE);
72164
72165 if (pDataStream == NULL) {
72166 return MA_INVALID_ARGS;
72167 }
72168
72169 if (streamResult != MA_SUCCESS) {
72170 return streamResult;
72171 }
72172
72173 /*
72174 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
72175 calculated when we initialized it on the job thread.
72176 */
72177 *pLength = pDataStream->totalLengthInPCMFrames;
72178 if (*pLength == 0) {
72179 return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */
72180 }
72181
72182 return MA_SUCCESS;
72183}
72184
72186{
72187 if (pDataStream == NULL) {
72188 return MA_INVALID_ARGS;
72189 }
72190
72191 return (ma_result)ma_atomic_load_i32(&pDataStream->result);
72192}
72193
72195{
72196 return ma_data_source_set_looping(pDataStream, isLooping);
72197}
72198
72200{
72201 if (pDataStream == NULL) {
72202 return MA_FALSE;
72203 }
72204
72205 return ma_atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */
72206}
72207
72209{
72210 ma_uint32 pageIndex0;
72211 ma_uint32 pageIndex1;
72212 ma_uint32 relativeCursor;
72213 ma_uint64 availableFrames;
72214
72215 if (pAvailableFrames == NULL) {
72216 return MA_INVALID_ARGS;
72217 }
72218
72219 *pAvailableFrames = 0;
72220
72221 if (pDataStream == NULL) {
72222 return MA_INVALID_ARGS;
72223 }
72224
72225 pageIndex0 = pDataStream->currentPageIndex;
72226 pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01;
72227 relativeCursor = pDataStream->relativeCursor;
72228
72229 availableFrames = 0;
72230 if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex0])) {
72231 availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor;
72232 if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex1])) {
72233 availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]);
72234 }
72235 }
72236
72237 *pAvailableFrames = availableFrames;
72238 return MA_SUCCESS;
72239}
72240
72241
72242static 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)
72243{
72244 if (pDataSource == NULL) {
72245 return MA_INVALID_ARGS;
72246 }
72247
72248 MA_ZERO_OBJECT(pDataSource);
72249
72250 if (pConfig == NULL) {
72251 return MA_INVALID_ARGS;
72252 }
72253
72254 if (pResourceManager == NULL) {
72255 return MA_INVALID_ARGS;
72256 }
72257
72258 pDataSource->flags = pConfig->flags;
72259 if (pConfig->isLooping) {
72261 }
72262
72263 return MA_SUCCESS;
72264}
72265
72266MA_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)
72267{
72268 ma_result result;
72269
72270 result = ma_resource_manager_data_source_preinit(pResourceManager, pConfig, pDataSource);
72271 if (result != MA_SUCCESS) {
72272 return result;
72273 }
72274
72275 /* The data source itself is just a data stream or a data buffer. */
72276 if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
72277 return ma_resource_manager_data_stream_init_ex(pResourceManager, pConfig, &pDataSource->backend.stream);
72278 } else {
72279 return ma_resource_manager_data_buffer_init_ex(pResourceManager, pConfig, &pDataSource->backend.buffer);
72280 }
72281}
72282
72283MA_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)
72284{
72285 ma_resource_manager_data_source_config config;
72286
72288 config.pFilePath = pName;
72289 config.flags = flags;
72290 config.pNotifications = pNotifications;
72291
72292 return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource);
72293}
72294
72295MA_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)
72296{
72297 ma_resource_manager_data_source_config config;
72298
72300 config.pFilePathW = pName;
72301 config.flags = flags;
72302 config.pNotifications = pNotifications;
72303
72304 return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource);
72305}
72306
72308{
72309 ma_result result;
72310 ma_resource_manager_data_source_config config;
72311
72312 if (pExistingDataSource == NULL) {
72313 return MA_INVALID_ARGS;
72314 }
72315
72317 config.flags = pExistingDataSource->flags;
72318
72319 result = ma_resource_manager_data_source_preinit(pResourceManager, &config, pDataSource);
72320 if (result != MA_SUCCESS) {
72321 return result;
72322 }
72323
72324 /* Copying can only be done from data buffers. Streams cannot be copied. */
72325 if ((pExistingDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
72326 return MA_INVALID_OPERATION;
72327 }
72328
72329 return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->backend.buffer, &pDataSource->backend.buffer);
72330}
72331
72333{
72334 if (pDataSource == NULL) {
72335 return MA_INVALID_ARGS;
72336 }
72337
72338 /* All we need to is uninitialize the underlying data buffer or data stream. */
72339 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
72341 } else {
72343 }
72344}
72345
72347{
72348 /* Safety. */
72349 if (pFramesRead != NULL) {
72350 *pFramesRead = 0;
72351 }
72352
72353 if (pDataSource == NULL) {
72354 return MA_INVALID_ARGS;
72355 }
72356
72357 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
72358 return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->backend.stream, pFramesOut, frameCount, pFramesRead);
72359 } else {
72360 return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->backend.buffer, pFramesOut, frameCount, pFramesRead);
72361 }
72362}
72363
72365{
72366 if (pDataSource == NULL) {
72367 return MA_INVALID_ARGS;
72368 }
72369
72370 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
72371 return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->backend.stream, frameIndex);
72372 } else {
72373 return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->backend.buffer, frameIndex);
72374 }
72375}
72376
72377MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount)
72378{
72379 if (pDataSource == NULL) {
72380 return MA_INVALID_ARGS;
72381 }
72382
72383 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
72384 return ma_resource_manager_data_stream_map(&pDataSource->backend.stream, ppFramesOut, pFrameCount);
72385 } else {
72386 return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */
72387 }
72388}
72389
72390MA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source* pDataSource, ma_uint64 frameCount)
72391{
72392 if (pDataSource == NULL) {
72393 return MA_INVALID_ARGS;
72394 }
72395
72396 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
72397 return ma_resource_manager_data_stream_unmap(&pDataSource->backend.stream, frameCount);
72398 } else {
72399 return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */
72400 }
72401}
72402
72403MA_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)
72404{
72405 if (pDataSource == NULL) {
72406 return MA_INVALID_ARGS;
72407 }
72408
72409 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
72410 return ma_resource_manager_data_stream_get_data_format(&pDataSource->backend.stream, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
72411 } else {
72412 return ma_resource_manager_data_buffer_get_data_format(&pDataSource->backend.buffer, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
72413 }
72414}
72415
72417{
72418 if (pDataSource == NULL) {
72419 return MA_INVALID_ARGS;
72420 }
72421
72422 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
72424 } else {
72426 }
72427}
72428
72430{
72431 if (pDataSource == NULL) {
72432 return MA_INVALID_ARGS;
72433 }
72434
72435 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
72437 } else {
72439 }
72440}
72441
72443{
72444 if (pDataSource == NULL) {
72445 return MA_INVALID_ARGS;
72446 }
72447
72448 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
72450 } else {
72452 }
72453}
72454
72456{
72457 if (pDataSource == NULL) {
72458 return MA_INVALID_ARGS;
72459 }
72460
72461 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
72462 return ma_resource_manager_data_stream_set_looping(&pDataSource->backend.stream, isLooping);
72463 } else {
72464 return ma_resource_manager_data_buffer_set_looping(&pDataSource->backend.buffer, isLooping);
72465 }
72466}
72467
72469{
72470 if (pDataSource == NULL) {
72471 return MA_FALSE;
72472 }
72473
72474 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
72476 } else {
72478 }
72479}
72480
72482{
72483 if (pAvailableFrames == NULL) {
72484 return MA_INVALID_ARGS;
72485 }
72486
72487 *pAvailableFrames = 0;
72488
72489 if (pDataSource == NULL) {
72490 return MA_INVALID_ARGS;
72491 }
72492
72493 if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
72494 return ma_resource_manager_data_stream_get_available_frames(&pDataSource->backend.stream, pAvailableFrames);
72495 } else {
72496 return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->backend.buffer, pAvailableFrames);
72497 }
72498}
72499
72500
72502{
72503 if (pResourceManager == NULL) {
72504 return MA_INVALID_ARGS;
72505 }
72506
72507 return ma_job_queue_post(&pResourceManager->jobQueue, pJob);
72508}
72509
72511{
72513 return ma_resource_manager_post_job(pResourceManager, &job);
72514}
72515
72517{
72518 if (pResourceManager == NULL) {
72519 return MA_INVALID_ARGS;
72520 }
72521
72522 return ma_job_queue_next(&pResourceManager->jobQueue, pJob);
72523}
72524
72525
72526static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob)
72527{
72528 ma_result result = MA_SUCCESS;
72529 ma_resource_manager* pResourceManager;
72530 ma_resource_manager_data_buffer_node* pDataBufferNode;
72531
72532 MA_ASSERT(pJob != NULL);
72533
72535 MA_ASSERT(pResourceManager != NULL);
72536
72538 MA_ASSERT(pDataBufferNode != NULL);
72539 MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */
72540
72541 /* 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. */
72542 if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) {
72543 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. */
72544 }
72545
72546 /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */
72547 if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) {
72548 result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */
72549 goto done;
72550 }
72551
72552 /*
72553 We're ready to start loading. Essentially what we're doing here is initializing the data supply
72554 of the node. Once this is complete, data buffers can have their connectors initialized which
72555 will allow then to have audio data read from them.
72556
72557 Note that when the data supply type has been moved away from "unknown", that is when other threads
72558 will determine that the node is available for data delivery and the data buffer connectors can be
72559 initialized. Therefore, it's important that it is set after the data supply has been initialized.
72560 */
72562 /*
72563 Decoding. This is the complex case because we're not going to be doing the entire decoding
72564 process here. Instead it's going to be split of multiple jobs and loaded in pages. The
72565 reason for this is to evenly distribute decoding time across multiple sounds, rather than
72566 having one huge sound hog all the available processing resources.
72567
72568 The first thing we do is initialize a decoder. This is allocated on the heap and is passed
72569 around to the paging jobs. When the last paging job has completed it's processing, it'll
72570 free the decoder for us.
72571
72572 This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job
72573 which is where the actual decoding work will be done. However, once this job is complete,
72574 the node will be in a state where data buffer connectors can be initialized.
72575 */
72576 ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */
72577 ma_job pageDataBufferNodeJob;
72578
72579 /* Allocate the decoder by initializing a decoded data supply. */
72580 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);
72581
72582 /*
72583 Don't ever propagate an MA_BUSY result code or else the resource manager will think the
72584 node is just busy decoding rather than in an error state. This should never happen, but
72585 including this logic for safety just in case.
72586 */
72587 if (result == MA_BUSY) {
72588 result = MA_ERROR;
72589 }
72590
72591 if (result != MA_SUCCESS) {
72593 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));
72594 } else {
72595 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
72596 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));
72597 #endif
72598 }
72599
72600 goto done;
72601 }
72602
72603 /*
72604 At this point the node's data supply is initialized and other threads can start initializing
72605 their data buffer connectors. However, no data will actually be available until we start to
72606 actually decode it. To do this, we need to post a paging job which is where the decoding
72607 work is done.
72608
72609 Note that if an error occurred at an earlier point, this section will have been skipped.
72610 */
72612 pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
72613 pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager;
72614 pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode;
72615 pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder;
72618
72619 /* The job has been set up so it can now be posted. */
72620 result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob);
72621
72622 /*
72623 When we get here, we want to make sure the result code is set to MA_BUSY. The reason for
72624 this is that the result will be copied over to the node's internal result variable. In
72625 this case, since the decoding is still in-progress, we need to make sure the result code
72626 is set to MA_BUSY.
72627 */
72628 if (result != MA_SUCCESS) {
72629 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));
72630 ma_decoder_uninit(pDecoder);
72631 ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
72632 } else {
72633 result = MA_BUSY;
72634 }
72635 } else {
72636 /* No decoding. This is the simple case. We need only read the file content into memory and we're done. */
72637 result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW);
72638 }
72639
72640
72641done:
72642 /* File paths are no longer needed. */
72645
72646 /*
72647 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
72648 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
72649 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
72650 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
72651 other error code would cause the buffer to look like it's in a state that it's not.
72652 */
72653 ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result);
72654
72655 /* At this point initialization is complete and we can signal the notification if any. */
72658 }
72661 }
72662
72663 /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */
72664 if (result != MA_BUSY) {
72667 }
72670 }
72671 }
72672
72673 /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */
72674 ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
72675
72676 /* A busy result should be considered successful from the point of view of the job system. */
72677 if (result == MA_BUSY) {
72678 result = MA_SUCCESS;
72679 }
72680
72681 return result;
72682}
72683
72684static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob)
72685{
72686 ma_resource_manager* pResourceManager;
72687 ma_resource_manager_data_buffer_node* pDataBufferNode;
72688
72689 MA_ASSERT(pJob != NULL);
72690
72692 MA_ASSERT(pResourceManager != NULL);
72693
72695 MA_ASSERT(pDataBufferNode != NULL);
72696
72697 if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) {
72698 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
72699 }
72700
72701 ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
72702
72703 /* The event needs to be signalled last. */
72706 }
72707
72710 }
72711
72712 ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
72713 return MA_SUCCESS;
72714}
72715
72716static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob)
72717{
72718 ma_result result = MA_SUCCESS;
72719 ma_resource_manager* pResourceManager;
72720 ma_resource_manager_data_buffer_node* pDataBufferNode;
72721
72722 MA_ASSERT(pJob != NULL);
72723
72725 MA_ASSERT(pResourceManager != NULL);
72726
72728 MA_ASSERT(pDataBufferNode != NULL);
72729
72730 if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) {
72731 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
72732 }
72733
72734 /* Don't do any more decoding if the data buffer has started the uninitialization process. */
72735 result = ma_resource_manager_data_buffer_node_result(pDataBufferNode);
72736 if (result != MA_BUSY) {
72737 goto done;
72738 }
72739
72740 /* We're ready to decode the next page. */
72741 result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder);
72742
72743 /*
72744 If we have a success code by this point, we want to post another job. We're going to set the
72745 result back to MA_BUSY to make it clear that there's still more to load.
72746 */
72747 if (result == MA_SUCCESS) {
72748 ma_job newJob;
72749 newJob = *pJob; /* Everything is the same as the input job, except the execution order. */
72750 newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); /* We need a fresh execution order. */
72751
72752 result = ma_resource_manager_post_job(pResourceManager, &newJob);
72753
72754 /* Since the sound isn't yet fully decoded we want the status to be set to busy. */
72755 if (result == MA_SUCCESS) {
72756 result = MA_BUSY;
72757 }
72758 }
72759
72760done:
72761 /* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */
72762 if (result != MA_BUSY) {
72765 }
72766
72767 /* If we reached the end we need to treat it as successful. */
72768 if (result == MA_AT_END) {
72769 result = MA_SUCCESS;
72770 }
72771
72772 /* Make sure we set the result of node in case some error occurred. */
72773 ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result);
72774
72775 /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */
72776 if (result != MA_BUSY) {
72779 }
72780
72783 }
72784 }
72785
72786 ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
72787 return result;
72788}
72789
72790
72791static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob)
72792{
72793 ma_result result = MA_SUCCESS;
72794 ma_resource_manager* pResourceManager;
72797 ma_bool32 isConnectorInitialized = MA_FALSE;
72798
72799 /*
72800 All we're doing here is checking if the node has finished loading. If not, we just re-post the job
72801 and keep waiting. Otherwise we increment the execution counter and set the buffer's result code.
72802 */
72803 MA_ASSERT(pJob != NULL);
72804
72806 MA_ASSERT(pDataBuffer != NULL);
72807
72808 pResourceManager = pDataBuffer->pResourceManager;
72809
72810 if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) {
72811 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. */
72812 }
72813
72814 /*
72815 First thing we need to do is check whether or not the data buffer is getting deleted. If so we
72816 just abort, but making sure we increment the execution pointer.
72817 */
72818 result = ma_resource_manager_data_buffer_result(pDataBuffer);
72819 if (result != MA_BUSY) {
72820 goto done; /* <-- This will ensure the execution pointer is incremented. */
72821 } else {
72822 result = MA_SUCCESS; /* <-- Make sure this is reset. */
72823 (void)result; /* <-- This is to suppress a static analysis diagnostic about "result" not being used. But for safety when I do future maintenance I don't want to delete that assignment. */
72824 }
72825
72826 /* Try initializing the connector if we haven't already. */
72827 isConnectorInitialized = ma_resource_manager_data_buffer_has_connector(pDataBuffer);
72828 if (isConnectorInitialized == MA_FALSE) {
72829 dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode);
72830
72831 if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) {
72832 /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */
72833 ma_resource_manager_data_source_config dataSourceConfig; /* For setting initial looping state and range. */
72839 dataSourceConfig.isLooping = pJob->data.resourceManager.loadDataBuffer.isLooping;
72840
72841 result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence);
72842 if (result != MA_SUCCESS) {
72843 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));
72844 goto done;
72845 }
72846 } else {
72847 /* 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. */
72848 }
72849 } else {
72850 /* The connector is already initialized. Nothing to do here. */
72851 }
72852
72853 /*
72854 If the data node is still loading, we need to repost the job and *not* increment the execution
72855 pointer (i.e. we need to not fall through to the "done" label).
72856
72857 There is a hole between here and the where the data connector is initialized where the data
72858 buffer node may have finished initializing. We need to check for this by checking the result of
72859 the data buffer node and whether or not we had an unknown data supply type at the time of
72860 trying to initialize the data connector.
72861 */
72862 result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode);
72863 if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) {
72864 return ma_resource_manager_post_job(pResourceManager, pJob);
72865 }
72866
72867done:
72868 /* Only move away from a busy code so that we don't trash any existing error codes. */
72869 ma_atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result);
72870
72871 /* Only signal the other threads after the result has been set just for cleanliness sake. */
72874 }
72877 }
72878
72879 /*
72880 If at this point the data buffer has not had it's connector initialized, it means the
72881 notification event was never signalled which means we need to signal it here.
72882 */
72883 if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE && result != MA_SUCCESS) {
72886 }
72889 }
72890 }
72891
72892 ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1);
72893 return result;
72894}
72895
72896static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob)
72897{
72898 ma_resource_manager* pResourceManager;
72900
72901 MA_ASSERT(pJob != NULL);
72902
72904 MA_ASSERT(pDataBuffer != NULL);
72905
72906 pResourceManager = pDataBuffer->pResourceManager;
72907
72908 if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) {
72909 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
72910 }
72911
72912 ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
72913
72914 /* The event needs to be signalled last. */
72917 }
72918
72921 }
72922
72923 ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1);
72924 return MA_SUCCESS;
72925}
72926
72927static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob)
72928{
72929 ma_result result = MA_SUCCESS;
72930 ma_decoder_config decoderConfig;
72931 ma_uint32 pageBufferSizeInBytes;
72932 ma_resource_manager* pResourceManager;
72934
72935 MA_ASSERT(pJob != NULL);
72936
72938 MA_ASSERT(pDataStream != NULL);
72939
72940 pResourceManager = pDataStream->pResourceManager;
72941
72942 if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) {
72943 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
72944 }
72945
72946 if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) {
72947 result = MA_INVALID_OPERATION; /* Most likely the data stream is being uninitialized. */
72948 goto done;
72949 }
72950
72951 /* We need to initialize the decoder first so we can determine the size of the pages. */
72952 decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager);
72953
72955 result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder);
72956 } else {
72957 result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder);
72958 }
72959 if (result != MA_SUCCESS) {
72960 goto done;
72961 }
72962
72963 /* Retrieve the total length of the file before marking the decoder as loaded. */
72965 result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames);
72966 if (result != MA_SUCCESS) {
72967 goto done; /* Failed to retrieve the length. */
72968 }
72969 } else {
72970 pDataStream->totalLengthInPCMFrames = 0;
72971 }
72972
72973 /*
72974 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
72975 and we don't want to have another thread trying to access the decoder while it's scanning.
72976 */
72977 pDataStream->isDecoderInitialized = MA_TRUE;
72978
72979 /* We have the decoder so we can now initialize our page buffer. */
72980 pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels);
72981
72982 pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks);
72983 if (pDataStream->pPageData == NULL) {
72984 ma_decoder_uninit(&pDataStream->decoder);
72985 result = MA_OUT_OF_MEMORY;
72986 goto done;
72987 }
72988
72989 /* Seek to our initial seek point before filling the initial pages. */
72991
72992 /* We have our decoder and our page buffer, so now we need to fill our pages. */
72993 ma_resource_manager_data_stream_fill_pages(pDataStream);
72994
72995 /* And now we're done. We want to make sure the result is MA_SUCCESS. */
72996 result = MA_SUCCESS;
72997
72998done:
73001
73002 /* 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). */
73003 ma_atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result);
73004
73005 /* Only signal the other threads after the result has been set just for cleanliness sake. */
73008 }
73011 }
73012
73013 ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);
73014 return result;
73015}
73016
73017static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob)
73018{
73019 ma_resource_manager* pResourceManager;
73021
73022 MA_ASSERT(pJob != NULL);
73023
73025 MA_ASSERT(pDataStream != NULL);
73026
73027 pResourceManager = pDataStream->pResourceManager;
73028
73029 if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) {
73030 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
73031 }
73032
73033 /* If our status is not MA_UNAVAILABLE we have a bug somewhere. */
73034 MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE);
73035
73036 if (pDataStream->isDecoderInitialized) {
73037 ma_decoder_uninit(&pDataStream->decoder);
73038 }
73039
73040 if (pDataStream->pPageData != NULL) {
73041 ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks);
73042 pDataStream->pPageData = NULL; /* Just in case... */
73043 }
73044
73045 ma_data_source_uninit(&pDataStream->ds);
73046
73047 /* The event needs to be signalled last. */
73050 }
73053 }
73054
73055 /*ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/
73056 return MA_SUCCESS;
73057}
73058
73059static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob)
73060{
73061 ma_result result = MA_SUCCESS;
73062 ma_resource_manager* pResourceManager;
73064
73065 MA_ASSERT(pJob != NULL);
73066
73068 MA_ASSERT(pDataStream != NULL);
73069
73070 pResourceManager = pDataStream->pResourceManager;
73071
73072 if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) {
73073 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
73074 }
73075
73076 /* For streams, the status should be MA_SUCCESS. */
73078 result = MA_INVALID_OPERATION;
73079 goto done;
73080 }
73081
73082 ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex);
73083
73084done:
73085 ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);
73086 return result;
73087}
73088
73089static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob)
73090{
73091 ma_result result = MA_SUCCESS;
73092 ma_resource_manager* pResourceManager;
73094
73095 MA_ASSERT(pJob != NULL);
73096
73098 MA_ASSERT(pDataStream != NULL);
73099
73100 pResourceManager = pDataStream->pResourceManager;
73101
73102 if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) {
73103 return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */
73104 }
73105
73106 /* For streams the status should be MA_SUCCESS for this to do anything. */
73107 if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) {
73108 result = MA_INVALID_OPERATION;
73109 goto done;
73110 }
73111
73112 /*
73113 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
73114 instead of initializing the decoder, we seek to a frame.
73115 */
73117
73118 /* After seeking we'll need to reload the pages. */
73119 ma_resource_manager_data_stream_fill_pages(pDataStream);
73120
73121 /* We need to let the public API know that we're done seeking. */
73122 ma_atomic_fetch_sub_32(&pDataStream->seekCounter, 1);
73123
73124done:
73125 ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);
73126 return result;
73127}
73128
73130{
73131 if (pResourceManager == NULL || pJob == NULL) {
73132 return MA_INVALID_ARGS;
73133 }
73134
73135 return ma_job_process(pJob);
73136}
73137
73139{
73140 ma_result result;
73141 ma_job job;
73142
73143 if (pResourceManager == NULL) {
73144 return MA_INVALID_ARGS;
73145 }
73146
73147 /* This will return MA_CANCELLED if the next job is a quit job. */
73148 result = ma_resource_manager_next_job(pResourceManager, &job);
73149 if (result != MA_SUCCESS) {
73150 return result;
73151 }
73152
73153 return ma_job_process(&job);
73154}
73155#else
73156/* 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. */
73157static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
73158static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
73159static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
73160static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); }
73161static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); }
73162static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
73163static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
73164static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
73165static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
73166#endif /* MA_NO_RESOURCE_MANAGER */
73167
73168
73169#ifndef MA_NO_NODE_GRAPH
73170
73171static ma_stack* ma_stack_init(size_t sizeInBytes, const ma_allocation_callbacks* pAllocationCallbacks)
73172{
73173 ma_stack* pStack;
73174
73175 if (sizeInBytes == 0) {
73176 return NULL;
73177 }
73178
73179 pStack = (ma_stack*)ma_malloc(sizeof(*pStack) - sizeof(pStack->_data) + sizeInBytes, pAllocationCallbacks);
73180 if (pStack == NULL) {
73181 return NULL;
73182 }
73183
73184 pStack->offset = 0;
73185 pStack->sizeInBytes = sizeInBytes;
73186
73187 return pStack;
73188}
73189
73190static void ma_stack_uninit(ma_stack* pStack, const ma_allocation_callbacks* pAllocationCallbacks)
73191{
73192 if (pStack == NULL) {
73193 return;
73194 }
73195
73196 ma_free(pStack, pAllocationCallbacks);
73197}
73198
73199static void* ma_stack_alloc(ma_stack* pStack, size_t sz)
73200{
73201 /* The size of the allocation is stored in the memory directly before the pointer. This needs to include padding to keep it aligned to ma_uintptr */
73202 void* p = (void*)((char*)pStack->_data + pStack->offset);
73203 size_t* pSize = (size_t*)p;
73204
73205 sz = (sz + (sizeof(ma_uintptr) - 1)) & ~(sizeof(ma_uintptr) - 1); /* Padding. */
73206 if (pStack->offset + sz + sizeof(size_t) > pStack->sizeInBytes) {
73207 return NULL; /* Out of memory. */
73208 }
73209
73210 pStack->offset += sz + sizeof(size_t);
73211
73212 *pSize = sz;
73213 return (void*)((char*)p + sizeof(size_t));
73214}
73215
73216static void ma_stack_free(ma_stack* pStack, void* p)
73217{
73218 size_t* pSize;
73219
73220 if (p == NULL) {
73221 return;
73222 }
73223
73224 pSize = (size_t*)p - 1;
73225 pStack->offset -= *pSize + sizeof(size_t);
73226}
73227
73228
73229
73230/* 10ms @ 48K = 480. Must never exceed 65535. */
73231#ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS
73232#define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480
73233#endif
73234
73235#ifndef MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL
73236#define MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL 524288
73237#endif
73238
73239static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime);
73240
73241MA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
73242{
73243 #ifndef MA_NO_GENERATION
73244 {
73245 ma_waveform_config waveformConfig;
73246 ma_waveform waveform;
73247
73248 waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400);
73249 ma_waveform_init(&waveformConfig, &waveform);
73250 ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL);
73251 }
73252 #else
73253 {
73254 (void)pFramesOut;
73255 (void)frameCount;
73256 (void)format;
73257 (void)channels;
73258 (void)sampleRate;
73259 #if defined(MA_DEBUG_OUTPUT)
73260 {
73261 #if _MSC_VER
73262 #pragma message ("ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.")
73263 #endif
73264 }
73265 #endif
73266 }
73267 #endif
73268}
73269
73270
73271
73272MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels)
73273{
73274 ma_node_graph_config config;
73275
73276 MA_ZERO_OBJECT(&config);
73277 config.channels = channels;
73278 config.processingSizeInFrames = 0;
73279
73280 return config;
73281}
73282
73283
73284static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading)
73285{
73286 MA_ASSERT(pNodeGraph != NULL);
73287 ma_atomic_exchange_32(&pNodeGraph->isReading, isReading);
73288}
73289
73290#if 0
73291static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph)
73292{
73293 MA_ASSERT(pNodeGraph != NULL);
73294 return ma_atomic_load_32(&pNodeGraph->isReading);
73295}
73296#endif
73297
73298
73299static void ma_node_graph_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73300{
73301 ma_node_graph* pNodeGraph = (ma_node_graph*)pNode;
73302 ma_uint64 framesRead;
73303
73304 ma_node_graph_read_pcm_frames(pNodeGraph, ppFramesOut[0], *pFrameCountOut, &framesRead);
73305
73306 *pFrameCountOut = (ma_uint32)framesRead; /* Safe cast. */
73307
73308 (void)ppFramesIn;
73309 (void)pFrameCountIn;
73310}
73311
73312static ma_node_vtable g_node_graph_node_vtable =
73313{
73314 ma_node_graph_node_process_pcm_frames,
73315 NULL, /* onGetRequiredInputFrameCount */
73316 0, /* 0 input buses. */
73317 1, /* 1 output bus. */
73318 0 /* Flags. */
73319};
73320
73321static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
73322{
73323 MA_ASSERT(pNode != NULL);
73324 MA_ASSERT(ma_node_get_input_bus_count(pNode) == 1);
73325 MA_ASSERT(ma_node_get_output_bus_count(pNode) == 1);
73326
73327 /* Input channel count needs to be the same as the output channel count. */
73328 MA_ASSERT(ma_node_get_input_channels(pNode, 0) == ma_node_get_output_channels(pNode, 0));
73329
73330 /* We don't need to do anything here because it's a passthrough. */
73331 (void)pNode;
73332 (void)ppFramesIn;
73333 (void)pFrameCountIn;
73334 (void)ppFramesOut;
73335 (void)pFrameCountOut;
73336
73337#if 0
73338 /* The data has already been mixed. We just need to move it to the output buffer. */
73339 if (ppFramesIn != NULL) {
73340 ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0));
73341 }
73342#endif
73343}
73344
73345static ma_node_vtable g_node_graph_endpoint_vtable =
73346{
73347 ma_node_graph_endpoint_process_pcm_frames,
73348 NULL, /* onGetRequiredInputFrameCount */
73349 1, /* 1 input bus. */
73350 1, /* 1 output bus. */
73351 MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */
73352};
73353
73354MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph)
73355{
73356 ma_result result;
73357 ma_node_config baseConfig;
73358 ma_node_config endpointConfig;
73359
73360 if (pNodeGraph == NULL) {
73361 return MA_INVALID_ARGS;
73362 }
73363
73364 MA_ZERO_OBJECT(pNodeGraph);
73365 pNodeGraph->processingSizeInFrames = pConfig->processingSizeInFrames;
73366
73367 /* Base node so we can use the node graph as a node into another graph. */
73368 baseConfig = ma_node_config_init();
73369 baseConfig.vtable = &g_node_graph_node_vtable;
73370 baseConfig.pOutputChannels = &pConfig->channels;
73371
73372 result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pNodeGraph->base);
73373 if (result != MA_SUCCESS) {
73374 return result;
73375 }
73376
73377
73378 /* Endpoint. */
73379 endpointConfig = ma_node_config_init();
73380 endpointConfig.vtable = &g_node_graph_endpoint_vtable;
73381 endpointConfig.pInputChannels = &pConfig->channels;
73382 endpointConfig.pOutputChannels = &pConfig->channels;
73383
73384 result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint);
73385 if (result != MA_SUCCESS) {
73386 ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks);
73387 return result;
73388 }
73389
73390
73391 /* Processing cache. */
73392 if (pConfig->processingSizeInFrames > 0) {
73393 pNodeGraph->pProcessingCache = (float*)ma_malloc(pConfig->processingSizeInFrames * pConfig->channels * sizeof(float), pAllocationCallbacks);
73394 if (pNodeGraph->pProcessingCache == NULL) {
73395 ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks);
73396 ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks);
73397 return MA_OUT_OF_MEMORY;
73398 }
73399 }
73400
73401
73402 /*
73403 We need a pre-mix stack. The size of this stack is configurable via the config. The default value depends on the channel count.
73404 */
73405 {
73406 size_t preMixStackSizeInBytes = pConfig->preMixStackSizeInBytes;
73407 if (preMixStackSizeInBytes == 0) {
73408 preMixStackSizeInBytes = pConfig->channels * MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL;
73409 }
73410
73411 pNodeGraph->pPreMixStack = ma_stack_init(preMixStackSizeInBytes, pAllocationCallbacks);
73412 if (pNodeGraph->pPreMixStack == NULL) {
73413 ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks);
73414 ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks);
73415 if (pNodeGraph->pProcessingCache != NULL) {
73416 ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks);
73417 }
73418
73419 return MA_OUT_OF_MEMORY;
73420 }
73421 }
73422
73423
73424 return MA_SUCCESS;
73425}
73426
73427MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks)
73428{
73429 if (pNodeGraph == NULL) {
73430 return;
73431 }
73432
73433 ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks);
73434 ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks);
73435
73436 if (pNodeGraph->pProcessingCache != NULL) {
73437 ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks);
73438 pNodeGraph->pProcessingCache = NULL;
73439 }
73440
73441 if (pNodeGraph->pPreMixStack != NULL) {
73442 ma_stack_uninit(pNodeGraph->pPreMixStack, pAllocationCallbacks);
73443 pNodeGraph->pPreMixStack = NULL;
73444 }
73445}
73446
73448{
73449 if (pNodeGraph == NULL) {
73450 return NULL;
73451 }
73452
73453 return &pNodeGraph->endpoint;
73454}
73455
73456MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
73457{
73458 ma_result result = MA_SUCCESS;
73459 ma_uint64 totalFramesRead;
73460 ma_uint32 channels;
73461
73462 if (pFramesRead != NULL) {
73463 *pFramesRead = 0; /* Safety. */
73464 }
73465
73466 if (pNodeGraph == NULL) {
73467 return MA_INVALID_ARGS;
73468 }
73469
73470 channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0);
73471
73472
73473 /* We'll be nice and try to do a full read of all frameCount frames. */
73474 totalFramesRead = 0;
73475 while (totalFramesRead < frameCount) {
73476 ma_uint32 framesJustRead;
73477 ma_uint64 framesToRead;
73478 float* pRunningFramesOut;
73479
73480 framesToRead = frameCount - totalFramesRead;
73481 if (framesToRead > 0xFFFFFFFF) {
73482 framesToRead = 0xFFFFFFFF;
73483 }
73484
73485 pRunningFramesOut = (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels);
73486
73487 /* If there's anything in the cache, consume that first. */
73488 if (pNodeGraph->processingCacheFramesRemaining > 0) {
73489 ma_uint32 framesToReadFromCache;
73490
73491 framesToReadFromCache = (ma_uint32)framesToRead;
73492 if (framesToReadFromCache > pNodeGraph->processingCacheFramesRemaining) {
73493 framesToReadFromCache = pNodeGraph->processingCacheFramesRemaining;
73494 }
73495
73496 MA_COPY_MEMORY(pRunningFramesOut, pNodeGraph->pProcessingCache, framesToReadFromCache * channels * sizeof(float));
73497 MA_MOVE_MEMORY(pNodeGraph->pProcessingCache, pNodeGraph->pProcessingCache + (framesToReadFromCache * channels), (pNodeGraph->processingCacheFramesRemaining - framesToReadFromCache) * channels * sizeof(float));
73498 pNodeGraph->processingCacheFramesRemaining -= framesToReadFromCache;
73499
73500 totalFramesRead += framesToReadFromCache;
73501 continue;
73502 } else {
73503 /*
73504 If processingSizeInFrames is non-zero, we need to make sure we always read in chunks of that size. If the frame count is less than
73505 that, we need to read into the cache and then continue on.
73506 */
73507 float* pReadDst = pRunningFramesOut;
73508
73509 if (pNodeGraph->processingSizeInFrames > 0) {
73510 if (framesToRead < pNodeGraph->processingSizeInFrames) {
73511 pReadDst = pNodeGraph->pProcessingCache; /* We need to read into the cache because otherwise we'll overflow the output buffer. */
73512 }
73513
73514 framesToRead = pNodeGraph->processingSizeInFrames;
73515 }
73516
73517 ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE);
73518 {
73519 result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, pReadDst, (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint));
73520 }
73521 ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE);
73522
73523 /*
73524 Do not increment the total frames read counter if we read into the cache. We use this to determine how many frames have
73525 been written to the final output buffer.
73526 */
73527 if (pReadDst == pNodeGraph->pProcessingCache) {
73528 /* We read into the cache. */
73529 pNodeGraph->processingCacheFramesRemaining = framesJustRead;
73530 } else {
73531 /* We read straight into the output buffer. */
73532 totalFramesRead += framesJustRead;
73533 }
73534
73535 if (result != MA_SUCCESS) {
73536 break;
73537 }
73538
73539 /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */
73540 if (framesJustRead == 0) {
73541 break;
73542 }
73543 }
73544 }
73545
73546 /* 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. */
73547 if (totalFramesRead < frameCount) {
73548 ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels);
73549 }
73550
73551 if (pFramesRead != NULL) {
73552 *pFramesRead = totalFramesRead;
73553 }
73554
73555 return result;
73556}
73557
73559{
73560 if (pNodeGraph == NULL) {
73561 return 0;
73562 }
73563
73564 return ma_node_get_output_channels(&pNodeGraph->endpoint, 0);
73565}
73566
73568{
73569 if (pNodeGraph == NULL) {
73570 return 0;
73571 }
73572
73573 return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */
73574}
73575
73577{
73578 if (pNodeGraph == NULL) {
73579 return MA_INVALID_ARGS;
73580 }
73581
73582 return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */
73583}
73584
73585
73586#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. */
73587
73588static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus)
73589{
73590 MA_ASSERT(pOutputBus != NULL);
73591 MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT);
73592 MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode));
73593 MA_ASSERT(channels < 256);
73594
73595 MA_ZERO_OBJECT(pOutputBus);
73596
73597 if (channels == 0) {
73598 return MA_INVALID_ARGS;
73599 }
73600
73601 pOutputBus->pNode = pNode;
73602 pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex;
73603 pOutputBus->channels = (ma_uint8)channels;
73604 pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */
73605 pOutputBus->volume = 1;
73606
73607 return MA_SUCCESS;
73608}
73609
73610static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus)
73611{
73612 ma_spinlock_lock(&pOutputBus->lock);
73613}
73614
73615static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus)
73616{
73617 ma_spinlock_unlock(&pOutputBus->lock);
73618}
73619
73620
73621static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus)
73622{
73623 return pOutputBus->channels;
73624}
73625
73626
73627static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead)
73628{
73629 if (hasRead) {
73630 ma_atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ);
73631 } else {
73632 ma_atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ);
73633 }
73634}
73635
73636static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus)
73637{
73638 return (ma_atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0;
73639}
73640
73641
73642static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached)
73643{
73644 ma_atomic_exchange_32(&pOutputBus->isAttached, isAttached);
73645}
73646
73647static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus)
73648{
73649 return ma_atomic_load_32(&pOutputBus->isAttached);
73650}
73651
73652
73653static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume)
73654{
73655 MA_ASSERT(pOutputBus != NULL);
73656
73657 if (volume < 0.0f) {
73658 volume = 0.0f;
73659 }
73660
73661 ma_atomic_exchange_f32(&pOutputBus->volume, volume);
73662
73663 return MA_SUCCESS;
73664}
73665
73666static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus)
73667{
73668 return ma_atomic_load_f32((float*)&pOutputBus->volume);
73669}
73670
73671
73672static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus)
73673{
73674 MA_ASSERT(pInputBus != NULL);
73675 MA_ASSERT(channels < 256);
73676
73677 MA_ZERO_OBJECT(pInputBus);
73678
73679 if (channels == 0) {
73680 return MA_INVALID_ARGS;
73681 }
73682
73683 pInputBus->channels = (ma_uint8)channels;
73684
73685 return MA_SUCCESS;
73686}
73687
73688static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus)
73689{
73690 MA_ASSERT(pInputBus != NULL);
73691
73692 ma_spinlock_lock(&pInputBus->lock);
73693}
73694
73695static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus)
73696{
73697 MA_ASSERT(pInputBus != NULL);
73698
73699 ma_spinlock_unlock(&pInputBus->lock);
73700}
73701
73702
73703static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus)
73704{
73705 ma_atomic_fetch_add_32(&pInputBus->nextCounter, 1);
73706}
73707
73708static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus)
73709{
73710 ma_atomic_fetch_sub_32(&pInputBus->nextCounter, 1);
73711}
73712
73713static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus)
73714{
73715 return ma_atomic_load_32(&pInputBus->nextCounter);
73716}
73717
73718
73719static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus)
73720{
73721 return pInputBus->channels;
73722}
73723
73724
73725static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
73726{
73727 MA_ASSERT(pInputBus != NULL);
73728 MA_ASSERT(pOutputBus != NULL);
73729
73730 /*
73731 Mark the output bus as detached first. This will prevent future iterations on the audio thread
73732 from iterating this output bus.
73733 */
73734 ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE);
73735
73736 /*
73737 We cannot use the output bus lock here since it'll be getting used at a higher level, but we do
73738 still need to use the input bus lock since we'll be updating pointers on two different output
73739 buses. The same rules apply here as the attaching case. Although we're using a lock here, we're
73740 *not* using a lock when iterating over the list in the audio thread. We therefore need to craft
73741 this in a way such that the iteration on the audio thread doesn't break.
73742
73743 The first thing to do is swap out the "next" pointer of the previous output bus with the
73744 new "next" output bus. This is the operation that matters for iteration on the audio thread.
73745 After that, the previous pointer on the new "next" pointer needs to be updated, after which
73746 point the linked list will be in a good state.
73747 */
73748 ma_node_input_bus_lock(pInputBus);
73749 {
73750 ma_node_output_bus* pOldPrev = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pPrev);
73751 ma_node_output_bus* pOldNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext);
73752
73753 if (pOldPrev != NULL) {
73754 ma_atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */
73755 }
73756 if (pOldNext != NULL) {
73757 ma_atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */
73758 }
73759 }
73760 ma_node_input_bus_unlock(pInputBus);
73761
73762 /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */
73763 ma_atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */
73764 ma_atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */
73765 pOutputBus->pInputNode = NULL;
73766 pOutputBus->inputNodeInputBusIndex = 0;
73767
73768
73769 /*
73770 For thread-safety reasons, we don't want to be returning from this straight away. We need to
73771 wait for the audio thread to finish with the output bus. There's two things we need to wait
73772 for. The first is the part that selects the next output bus in the list, and the other is the
73773 part that reads from the output bus. Basically all we're doing is waiting for the input bus
73774 to stop referencing the output bus.
73775
73776 We're doing this part last because we want the section above to run while the audio thread
73777 is finishing up with the output bus, just for efficiency reasons. We marked the output bus as
73778 detached right at the top of this function which is going to prevent the audio thread from
73779 iterating the output bus again.
73780 */
73781
73782 /* Part 1: Wait for the current iteration to complete. */
73783 while (ma_node_input_bus_get_next_counter(pInputBus) > 0) {
73784 ma_yield();
73785 }
73786
73787 /* Part 2: Wait for any reads to complete. */
73788 while (ma_atomic_load_32(&pOutputBus->refCount) > 0) {
73789 ma_yield();
73790 }
73791
73792 /*
73793 At this point we're done detaching and we can be guaranteed that the audio thread is not going
73794 to attempt to reference this output bus again (until attached again).
73795 */
73796}
73797
73798#if 0 /* Not used at the moment, but leaving here in case I need it later. */
73799static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
73800{
73801 MA_ASSERT(pInputBus != NULL);
73802 MA_ASSERT(pOutputBus != NULL);
73803
73804 ma_node_output_bus_lock(pOutputBus);
73805 {
73806 ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus);
73807 }
73808 ma_node_output_bus_unlock(pOutputBus);
73809}
73810#endif
73811
73812static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex)
73813{
73814 MA_ASSERT(pInputBus != NULL);
73815 MA_ASSERT(pOutputBus != NULL);
73816
73817 ma_node_output_bus_lock(pOutputBus);
73818 {
73819 ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pInputNode);
73820
73821 /* Detach from any existing attachment first if necessary. */
73822 if (pOldInputNode != NULL) {
73823 ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus);
73824 }
73825
73826 /*
73827 At this point we can be sure the output bus is not attached to anything. The linked list in the
73828 old input bus has been updated so that pOutputBus will not get iterated again.
73829 */
73830 pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */
73831 pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex;
73832
73833 /*
73834 Now we need to attach the output bus to the linked list. This involves updating two pointers on
73835 two different output buses so I'm going to go ahead and keep this simple and just use a lock.
73836 There are ways to do this without a lock, but it's just too hard to maintain for its value.
73837
73838 Although we're locking here, it's important to remember that we're *not* locking when iterating
73839 and reading audio data since that'll be running on the audio thread. As a result we need to be
73840 careful how we craft this so that we don't break iteration. What we're going to do is always
73841 attach the new item so that it becomes the first item in the list. That way, as we're iterating
73842 we won't break any links in the list and iteration will continue safely. The detaching case will
73843 also be crafted in a way as to not break list iteration. It's important to remember to use
73844 atomic exchanges here since no locking is happening on the audio thread during iteration.
73845 */
73846 ma_node_input_bus_lock(pInputBus);
73847 {
73848 ma_node_output_bus* pNewPrev = &pInputBus->head;
73849 ma_node_output_bus* pNewNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext);
73850
73851 /* Update the local output bus. */
73852 ma_atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev);
73853 ma_atomic_exchange_ptr(&pOutputBus->pNext, pNewNext);
73854
73855 /* Update the other output buses to point back to the local output bus. */
73856 ma_atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */
73857
73858 /* Do the previous pointer last. This is only used for detachment. */
73859 if (pNewNext != NULL) {
73860 ma_atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus);
73861 }
73862 }
73863 ma_node_input_bus_unlock(pInputBus);
73864
73865 /*
73866 Mark the node as attached last. This is used to controlling whether or the output bus will be
73867 iterated on the audio thread. Mainly required for detachment purposes.
73868 */
73869 ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE);
73870 }
73871 ma_node_output_bus_unlock(pOutputBus);
73872}
73873
73874static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
73875{
73876 ma_node_output_bus* pNext;
73877
73878 MA_ASSERT(pInputBus != NULL);
73879
73880 if (pOutputBus == NULL) {
73881 return NULL;
73882 }
73883
73884 ma_node_input_bus_next_begin(pInputBus);
73885 {
73886 pNext = pOutputBus;
73887 for (;;) {
73888 pNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pNext->pNext);
73889 if (pNext == NULL) {
73890 break; /* Reached the end. */
73891 }
73892
73893 if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) {
73894 continue; /* The node is not attached. Keep checking. */
73895 }
73896
73897 /* The next node has been selected. */
73898 break;
73899 }
73900
73901 /* We need to increment the reference count of the selected node. */
73902 if (pNext != NULL) {
73903 ma_atomic_fetch_add_32(&pNext->refCount, 1);
73904 }
73905
73906 /* The previous node is no longer being referenced. */
73907 ma_atomic_fetch_sub_32(&pOutputBus->refCount, 1);
73908 }
73909 ma_node_input_bus_next_end(pInputBus);
73910
73911 return pNext;
73912}
73913
73914static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus)
73915{
73916 return ma_node_input_bus_next(pInputBus, &pInputBus->head);
73917}
73918
73919
73920
73921static 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)
73922{
73923 ma_result result = MA_SUCCESS;
73924 ma_node_output_bus* pOutputBus;
73925 ma_node_output_bus* pFirst;
73926 ma_uint32 inputChannels;
73927 ma_bool32 doesOutputBufferHaveContent = MA_FALSE;
73928
73929 /*
73930 This will be called from the audio thread which means we can't be doing any locking. Basically,
73931 this function will not perform any locking, whereas attaching and detaching will, but crafted in
73932 such a way that we don't need to perform any locking here. The important thing to remember is
73933 to always iterate in a forward direction.
73934
73935 In order to process any data we need to first read from all input buses. That's where this
73936 function comes in. This iterates over each of the attachments and accumulates/mixes them. We
73937 also convert the channels to the nodes output channel count before mixing. We want to do this
73938 channel conversion so that the caller of this function can invoke the processing callback
73939 without having to do it themselves.
73940
73941 When we iterate over each of the attachments on the input bus, we need to read as much data as
73942 we can from each of them so that we don't end up with holes between each of the attachments. To
73943 do this, we need to read from each attachment in a loop and read as many frames as we can, up
73944 to `frameCount`.
73945 */
73946 MA_ASSERT(pInputNode != NULL);
73947 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. */
73948
73949 *pFramesRead = 0; /* Safety. */
73950
73951 inputChannels = ma_node_input_bus_get_channels(pInputBus);
73952
73953 /*
73954 We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They
73955 are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first()
73956 once per iteration, however we have an optimization to checks whether or not it's the first item in
73957 the list. We therefore need to store a pointer to the first item rather than repeatedly calling
73958 ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it
73959 after calling ma_node_input_bus_next(), which we won't be.
73960 */
73961 pFirst = ma_node_input_bus_first(pInputBus);
73962 if (pFirst == NULL) {
73963 return MA_SUCCESS; /* No attachments. Read nothing. */
73964 }
73965
73966 for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) {
73967 ma_uint32 framesProcessed = 0;
73968 ma_bool32 isSilentOutput = MA_FALSE;
73969
73970 MA_ASSERT(pOutputBus->pNode != NULL);
73971 MA_ASSERT(((ma_node_base*)pOutputBus->pNode)->vtable != NULL);
73972
73973 isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0;
73974
73975 if (pFramesOut != NULL) {
73976 /* Read. */
73977 while (framesProcessed < frameCount) {
73978 float* pRunningFramesOut;
73979 ma_uint32 framesToRead;
73980 ma_uint32 framesJustRead = 0;
73981
73982 framesToRead = frameCount - framesProcessed;
73983 pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels);
73984
73985 if (doesOutputBufferHaveContent == MA_FALSE) {
73986 /* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */
73987 result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed);
73988 } else {
73989 /* Slow path. Not the first attachment. Mixing required. */
73990 ma_uint32 preMixBufferCapInFrames = ((ma_node_base*)pInputNode)->cachedDataCapInFramesPerBus;
73991 float* pPreMixBuffer = (float*)ma_stack_alloc(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, preMixBufferCapInFrames * inputChannels * sizeof(float));
73992
73993 if (pPreMixBuffer == NULL) {
73994 /*
73995 If you're hitting this assert it means you've got an unusually deep chain of nodes, you've got an excessively large processing
73996 size, or you have a combination of both, and as a result have run out of stack space. You can increase this using the
73997 preMixStackSizeInBytes variable in ma_node_graph_config. If you're using ma_engine, you can do it via the preMixStackSizeInBytes
73998 variable in ma_engine_config. It defaults to 512KB per output channel.
73999 */
74000 MA_ASSERT(MA_FALSE);
74001 } else {
74002 if (framesToRead > preMixBufferCapInFrames) {
74003 framesToRead = preMixBufferCapInFrames;
74004 }
74005
74006 result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pPreMixBuffer, framesToRead, &framesJustRead, globalTime + framesProcessed);
74007 if (result == MA_SUCCESS || result == MA_AT_END) {
74008 if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */
74009 ma_mix_pcm_frames_f32(pRunningFramesOut, pPreMixBuffer, framesJustRead, inputChannels, /*volume*/1);
74010 }
74011 }
74012
74013 /* The pre-mix buffer is no longer required. */
74014 ma_stack_free(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, pPreMixBuffer);
74015 pPreMixBuffer = NULL;
74016 }
74017 }
74018
74019 framesProcessed += framesJustRead;
74020
74021 /* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */
74022 if (result != MA_SUCCESS) {
74023 break;
74024 }
74025
74026 /* If we didn't read anything, abort so we don't get stuck in a loop. */
74027 if (framesJustRead == 0) {
74028 break;
74029 }
74030 }
74031
74032 /* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */
74033 if (pOutputBus == pFirst && framesProcessed < frameCount) {
74034 ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels);
74035 }
74036
74037 if (isSilentOutput == MA_FALSE) {
74038 doesOutputBufferHaveContent = MA_TRUE;
74039 }
74040 } else {
74041 /* Seek. */
74042 ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime);
74043 }
74044 }
74045
74046 /* If we didn't output anything, output silence. */
74047 if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) {
74048 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, inputChannels);
74049 }
74050
74051 /* In this path we always "process" the entire amount. */
74052 *pFramesRead = frameCount;
74053
74054 return result;
74055}
74056
74057
74058MA_API ma_node_config ma_node_config_init(void)
74059{
74060 ma_node_config config;
74061
74062 MA_ZERO_OBJECT(&config);
74063 config.initialState = ma_node_state_started; /* Nodes are started by default. */
74066
74067 return config;
74068}
74069
74070static ma_uint16 ma_node_config_get_cache_size_in_frames(const ma_node_config* pConfig, const ma_node_graph* pNodeGraph)
74071{
74072 ma_uint32 cacheSizeInFrames;
74073
74074 (void)pConfig;
74075
74076 if (pNodeGraph->processingSizeInFrames > 0) {
74077 cacheSizeInFrames = pNodeGraph->processingSizeInFrames;
74078 } else {
74079 cacheSizeInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS;
74080 }
74081
74082 if (cacheSizeInFrames > 0xFFFF) {
74083 cacheSizeInFrames = 0xFFFF;
74084 }
74085
74086 return (ma_uint16)cacheSizeInFrames;
74087}
74088
74089
74090
74091static ma_result ma_node_detach_full(ma_node* pNode);
74092
74093static float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex)
74094{
74095 ma_node_base* pNodeBase = (ma_node_base*)pNode;
74096 ma_uint32 iInputBus;
74097 float* pBasePtr;
74098
74099 MA_ASSERT(pNodeBase != NULL);
74100
74101 /* Input data is stored at the front of the buffer. */
74102 pBasePtr = pNodeBase->pCachedData;
74103 for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) {
74104 pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]);
74105 }
74106
74107 return pBasePtr;
74108}
74109
74110static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex)
74111{
74112 ma_node_base* pNodeBase = (ma_node_base*)pNode;
74113 ma_uint32 iInputBus;
74114 ma_uint32 iOutputBus;
74115 float* pBasePtr;
74116
74117 MA_ASSERT(pNodeBase != NULL);
74118
74119 /* Cached output data starts after the input data. */
74120 pBasePtr = pNodeBase->pCachedData;
74121 for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) {
74122 pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]);
74123 }
74124
74125 for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) {
74126 pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]);
74127 }
74128
74129 return pBasePtr;
74130}
74131
74132
74133typedef struct
74134{
74135 size_t sizeInBytes;
74136 size_t inputBusOffset;
74137 size_t outputBusOffset;
74138 size_t cachedDataOffset;
74139 ma_uint32 inputBusCount; /* So it doesn't have to be calculated twice. */
74140 ma_uint32 outputBusCount; /* So it doesn't have to be calculated twice. */
74141} ma_node_heap_layout;
74142
74143static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount)
74144{
74145 ma_uint32 inputBusCount;
74146 ma_uint32 outputBusCount;
74147
74148 MA_ASSERT(pConfig != NULL);
74149 MA_ASSERT(pInputBusCount != NULL);
74150 MA_ASSERT(pOutputBusCount != NULL);
74151
74152 /* 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. */
74154 inputBusCount = pConfig->inputBusCount;
74155 } else {
74156 inputBusCount = pConfig->vtable->inputBusCount;
74157
74158 if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) {
74159 return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */
74160 }
74161 }
74162
74164 outputBusCount = pConfig->outputBusCount;
74165 } else {
74166 outputBusCount = pConfig->vtable->outputBusCount;
74167
74168 if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) {
74169 return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */
74170 }
74171 }
74172
74173 /* Bus counts must be within limits. */
74174 if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) {
74175 return MA_INVALID_ARGS;
74176 }
74177
74178
74179 /* We must have channel counts for each bus. */
74180 if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) {
74181 return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */
74182 }
74183
74184
74185 /* Some special rules for passthrough nodes. */
74186 if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {
74187 if ((pConfig->vtable->inputBusCount != 0 && pConfig->vtable->inputBusCount != 1) || pConfig->vtable->outputBusCount != 1) {
74188 return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 output bus and either 0 or 1 input bus. */
74189 }
74190
74191 if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) {
74192 return MA_INVALID_ARGS; /* Passthrough nodes must have the same number of channels between input and output nodes. */
74193 }
74194 }
74195
74196
74197 *pInputBusCount = inputBusCount;
74198 *pOutputBusCount = outputBusCount;
74199
74200 return MA_SUCCESS;
74201}
74202
74203static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout)
74204{
74205 ma_result result;
74206 ma_uint32 inputBusCount;
74207 ma_uint32 outputBusCount;
74208
74209 MA_ASSERT(pHeapLayout != NULL);
74210
74211 MA_ZERO_OBJECT(pHeapLayout);
74212
74213 if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) {
74214 return MA_INVALID_ARGS;
74215 }
74216
74217 result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount);
74218 if (result != MA_SUCCESS) {
74219 return result;
74220 }
74221
74222 pHeapLayout->sizeInBytes = 0;
74223
74224 /* Input buses. */
74225 if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) {
74226 pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes;
74227 pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount);
74228 } else {
74229 pHeapLayout->inputBusOffset = MA_SIZE_MAX; /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */
74230 }
74231
74232 /* Output buses. */
74233 if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) {
74234 pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes;
74235 pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount);
74236 } else {
74237 pHeapLayout->outputBusOffset = MA_SIZE_MAX;
74238 }
74239
74240 /*
74241 Cached audio data.
74242
74243 We need to allocate memory for caching both input and output data. We have an optimization
74244 where no caching is necessary for specific conditions:
74245
74246 - The node has 0 inputs and 1 output.
74247
74248 When a node meets the above conditions, no cache is allocated.
74249
74250 The size choice for this buffer is a little bit finicky. We don't want to be too wasteful by
74251 allocating too much, but at the same time we want it be large enough so that enough frames can
74252 be processed for each call to ma_node_read_pcm_frames() so that it keeps things efficient. For
74253 now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile
74254 time. It might also be worth investigating whether or not this can be configured at run time.
74255 */
74256 if (inputBusCount == 0 && outputBusCount == 1) {
74257 /* Fast path. No cache needed. */
74258 pHeapLayout->cachedDataOffset = MA_SIZE_MAX;
74259 } else {
74260 /* Slow path. Cache needed. */
74261 size_t cachedDataSizeInBytes = 0;
74262 ma_uint32 cacheCapInFrames;
74263 ma_uint32 iBus;
74264
74265 /* The capacity of the cache is based on our callback processing size. */
74266 cacheCapInFrames = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph);
74267
74268 for (iBus = 0; iBus < inputBusCount; iBus += 1) {
74269 cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]);
74270 }
74271
74272 for (iBus = 0; iBus < outputBusCount; iBus += 1) {
74273 cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]);
74274 }
74275
74276 pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes;
74277 pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes);
74278 }
74279
74280
74281 /*
74282 Not technically part of the heap, but we can output the input and output bus counts so we can
74283 avoid a redundant call to ma_node_translate_bus_counts().
74284 */
74285 pHeapLayout->inputBusCount = inputBusCount;
74286 pHeapLayout->outputBusCount = outputBusCount;
74287
74288 /* Make sure allocation size is aligned. */
74289 pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
74290
74291 return MA_SUCCESS;
74292}
74293
74294MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes)
74295{
74296 ma_result result;
74297 ma_node_heap_layout heapLayout;
74298
74299 if (pHeapSizeInBytes == NULL) {
74300 return MA_INVALID_ARGS;
74301 }
74302
74303 *pHeapSizeInBytes = 0;
74304
74305 result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout);
74306 if (result != MA_SUCCESS) {
74307 return result;
74308 }
74309
74310 *pHeapSizeInBytes = heapLayout.sizeInBytes;
74311
74312 return MA_SUCCESS;
74313}
74314
74315MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode)
74316{
74317 ma_node_base* pNodeBase = (ma_node_base*)pNode;
74318 ma_result result;
74319 ma_node_heap_layout heapLayout;
74320 ma_uint32 iInputBus;
74321 ma_uint32 iOutputBus;
74322
74323 if (pNodeBase == NULL) {
74324 return MA_INVALID_ARGS;
74325 }
74326
74327 MA_ZERO_OBJECT(pNodeBase);
74328
74329 result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout);
74330 if (result != MA_SUCCESS) {
74331 return result;
74332 }
74333
74334 pNodeBase->_pHeap = pHeap;
74335 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
74336
74337 pNodeBase->pNodeGraph = pNodeGraph;
74338 pNodeBase->vtable = pConfig->vtable;
74339 pNodeBase->state = pConfig->initialState;
74340 pNodeBase->stateTimes[ma_node_state_started] = 0;
74341 pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */
74342 pNodeBase->inputBusCount = heapLayout.inputBusCount;
74343 pNodeBase->outputBusCount = heapLayout.outputBusCount;
74344
74345 if (heapLayout.inputBusOffset != MA_SIZE_MAX) {
74346 pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset);
74347 } else {
74348 pNodeBase->pInputBuses = pNodeBase->_inputBuses;
74349 }
74350
74351 if (heapLayout.outputBusOffset != MA_SIZE_MAX) {
74352 pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.outputBusOffset);
74353 } else {
74354 pNodeBase->pOutputBuses = pNodeBase->_outputBuses;
74355 }
74356
74357 if (heapLayout.cachedDataOffset != MA_SIZE_MAX) {
74358 pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset);
74359 pNodeBase->cachedDataCapInFramesPerBus = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph);
74360 } else {
74361 pNodeBase->pCachedData = NULL;
74362 }
74363
74364
74365 /* We need to run an initialization step for each input and output bus. */
74366 for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) {
74367 result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]);
74368 if (result != MA_SUCCESS) {
74369 return result;
74370 }
74371 }
74372
74373 for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) {
74374 result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]);
74375 if (result != MA_SUCCESS) {
74376 return result;
74377 }
74378 }
74379
74380
74381 /* The cached data needs to be initialized to silence (or a sine wave tone if we're debugging). */
74382 if (pNodeBase->pCachedData != NULL) {
74383 ma_uint32 iBus;
74384
74385 #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. */
74386 /* For safety we'll go ahead and default the buffer to silence. */
74387 for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) {
74388 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]));
74389 }
74390 for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) {
74391 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]));
74392 }
74393 #else
74394 /* For debugging. Default to a sine wave. */
74395 for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) {
74396 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);
74397 }
74398 for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) {
74399 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);
74400 }
74401 #endif
74402 }
74403
74404 return MA_SUCCESS;
74405}
74406
74407MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode)
74408{
74409 ma_result result;
74410 size_t heapSizeInBytes;
74411 void* pHeap;
74412
74413 result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes);
74414 if (result != MA_SUCCESS) {
74415 return result;
74416 }
74417
74418 if (heapSizeInBytes > 0) {
74419 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
74420 if (pHeap == NULL) {
74421 return MA_OUT_OF_MEMORY;
74422 }
74423 } else {
74424 pHeap = NULL;
74425 }
74426
74427 result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode);
74428 if (result != MA_SUCCESS) {
74429 ma_free(pHeap, pAllocationCallbacks);
74430 return result;
74431 }
74432
74433 ((ma_node_base*)pNode)->_ownsHeap = MA_TRUE;
74434 return MA_SUCCESS;
74435}
74436
74437MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
74438{
74439 ma_node_base* pNodeBase = (ma_node_base*)pNode;
74440
74441 if (pNodeBase == NULL) {
74442 return;
74443 }
74444
74445 /*
74446 The first thing we need to do is fully detach the node. This will detach all inputs and
74447 outputs. We need to do this first because it will sever the connection with the node graph and
74448 allow us to complete uninitialization without needing to worry about thread-safety with the
74449 audio thread. The detachment process will wait for any local processing of the node to finish.
74450 */
74451 ma_node_detach_full(pNode);
74452
74453 /*
74454 At this point the node should be completely unreferenced by the node graph and we can finish up
74455 the uninitialization process without needing to worry about thread-safety.
74456 */
74457 if (pNodeBase->_ownsHeap) {
74458 ma_free(pNodeBase->_pHeap, pAllocationCallbacks);
74459 }
74460}
74461
74463{
74464 if (pNode == NULL) {
74465 return NULL;
74466 }
74467
74468 return ((const ma_node_base*)pNode)->pNodeGraph;
74469}
74470
74472{
74473 if (pNode == NULL) {
74474 return 0;
74475 }
74476
74477 return ((ma_node_base*)pNode)->inputBusCount;
74478}
74479
74481{
74482 if (pNode == NULL) {
74483 return 0;
74484 }
74485
74486 return ((ma_node_base*)pNode)->outputBusCount;
74487}
74488
74489
74490MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex)
74491{
74492 const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
74493
74494 if (pNode == NULL) {
74495 return 0;
74496 }
74497
74498 if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) {
74499 return 0; /* Invalid bus index. */
74500 }
74501
74502 return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]);
74503}
74504
74505MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex)
74506{
74507 const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
74508
74509 if (pNode == NULL) {
74510 return 0;
74511 }
74512
74513 if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
74514 return 0; /* Invalid bus index. */
74515 }
74516
74517 return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]);
74518}
74519
74520
74521static ma_result ma_node_detach_full(ma_node* pNode)
74522{
74523 ma_node_base* pNodeBase = (ma_node_base*)pNode;
74524 ma_uint32 iInputBus;
74525
74526 if (pNodeBase == NULL) {
74527 return MA_INVALID_ARGS;
74528 }
74529
74530 /*
74531 Make sure the node is completely detached first. This will not return until the output bus is
74532 guaranteed to no longer be referenced by the audio thread.
74533 */
74535
74536 /*
74537 At this point all output buses will have been detached from the graph and we can be guaranteed
74538 that none of its input nodes will be getting processed by the graph. We can detach these
74539 without needing to worry about the audio thread touching them.
74540 */
74541 for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) {
74542 ma_node_input_bus* pInputBus;
74543 ma_node_output_bus* pOutputBus;
74544
74545 pInputBus = &pNodeBase->pInputBuses[iInputBus];
74546
74547 /*
74548 This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those
74549 functions are specifically for the audio thread. We'll instead just manually iterate using standard
74550 linked list logic. We don't need to worry about the audio thread referencing these because the step
74551 above severed the connection to the graph.
74552 */
74553 for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext)) {
74554 ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */
74555 }
74556 }
74557
74558 return MA_SUCCESS;
74559}
74560
74562{
74563 ma_result result = MA_SUCCESS;
74564 ma_node_base* pNodeBase = (ma_node_base*)pNode;
74565 ma_node_base* pInputNodeBase;
74566
74567 if (pNode == NULL) {
74568 return MA_INVALID_ARGS;
74569 }
74570
74571 if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
74572 return MA_INVALID_ARGS; /* Invalid output bus index. */
74573 }
74574
74575 /* We need to lock the output bus because we need to inspect the input node and grab its input bus. */
74576 ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]);
74577 {
74578 pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode;
74579 if (pInputNodeBase != NULL) {
74580 ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]);
74581 }
74582 }
74583 ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]);
74584
74585 return result;
74586}
74587
74589{
74590 ma_uint32 iOutputBus;
74591
74592 if (pNode == NULL) {
74593 return MA_INVALID_ARGS;
74594 }
74595
74596 for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) {
74597 ma_node_detach_output_bus(pNode, iOutputBus);
74598 }
74599
74600 return MA_SUCCESS;
74601}
74602
74603MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex)
74604{
74605 ma_node_base* pNodeBase = (ma_node_base*)pNode;
74606 ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode;
74607
74608 if (pNodeBase == NULL || pOtherNodeBase == NULL) {
74609 return MA_INVALID_ARGS;
74610 }
74611
74612 if (pNodeBase == pOtherNodeBase) {
74613 return MA_INVALID_OPERATION; /* Cannot attach a node to itself. */
74614 }
74615
74616 if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) {
74617 return MA_INVALID_OPERATION; /* Invalid bus index. */
74618 }
74619
74620 /* The output channel count of the output node must be the same as the input channel count of the input node. */
74621 if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) {
74622 return MA_INVALID_OPERATION; /* Channel count is incompatible. */
74623 }
74624
74625 /* This will deal with detaching if the output bus is already attached to something. */
74626 ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex);
74627
74628 return MA_SUCCESS;
74629}
74630
74631MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume)
74632{
74633 ma_node_base* pNodeBase = (ma_node_base*)pNode;
74634
74635 if (pNodeBase == NULL) {
74636 return MA_INVALID_ARGS;
74637 }
74638
74639 if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
74640 return MA_INVALID_ARGS; /* Invalid bus index. */
74641 }
74642
74643 return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume);
74644}
74645
74646MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex)
74647{
74648 const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
74649
74650 if (pNodeBase == NULL) {
74651 return 0;
74652 }
74653
74654 if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
74655 return 0; /* Invalid bus index. */
74656 }
74657
74658 return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]);
74659}
74660
74662{
74663 ma_node_base* pNodeBase = (ma_node_base*)pNode;
74664
74665 if (pNodeBase == NULL) {
74666 return MA_INVALID_ARGS;
74667 }
74668
74669 ma_atomic_exchange_i32(&pNodeBase->state, state);
74670
74671 return MA_SUCCESS;
74672}
74673
74675{
74676 const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
74677
74678 if (pNodeBase == NULL) {
74679 return ma_node_state_stopped;
74680 }
74681
74682 return (ma_node_state)ma_atomic_load_i32(&pNodeBase->state);
74683}
74684
74686{
74687 if (pNode == NULL) {
74688 return MA_INVALID_ARGS;
74689 }
74690
74691 /* Validation check for safety since we'll be using this as an index into stateTimes[]. */
74692 if (state != ma_node_state_started && state != ma_node_state_stopped) {
74693 return MA_INVALID_ARGS;
74694 }
74695
74696 ma_atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime);
74697
74698 return MA_SUCCESS;
74699}
74700
74702{
74703 if (pNode == NULL) {
74704 return 0;
74705 }
74706
74707 /* Validation check for safety since we'll be using this as an index into stateTimes[]. */
74708 if (state != ma_node_state_started && state != ma_node_state_stopped) {
74709 return 0;
74710 }
74711
74712 return ma_atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]);
74713}
74714
74716{
74717 if (pNode == NULL) {
74718 return ma_node_state_stopped;
74719 }
74720
74721 return ma_node_get_state_by_time_range(pNode, globalTime, globalTime);
74722}
74723
74724MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd)
74725{
74726 ma_node_state state;
74727
74728 if (pNode == NULL) {
74729 return ma_node_state_stopped;
74730 }
74731
74732 state = ma_node_get_state(pNode);
74733
74734 /* An explicitly stopped node is always stopped. */
74735 if (state == ma_node_state_stopped) {
74736 return ma_node_state_stopped;
74737 }
74738
74739 /*
74740 Getting here means the node is marked as started, but it may still not be truly started due to
74741 its start time not having been reached yet. Also, the stop time may have also been reached in
74742 which case it'll be considered stopped.
74743 */
74744 if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) {
74745 return ma_node_state_stopped; /* Start time has not yet been reached. */
74746 }
74747
74748 if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) {
74749 return ma_node_state_stopped; /* Stop time has been reached. */
74750 }
74751
74752 /* Getting here means the node is marked as started and is within its start/stop times. */
74753 return ma_node_state_started;
74754}
74755
74757{
74758 if (pNode == NULL) {
74759 return 0;
74760 }
74761
74762 return ma_atomic_load_64(&((ma_node_base*)pNode)->localTime);
74763}
74764
74766{
74767 if (pNode == NULL) {
74768 return MA_INVALID_ARGS;
74769 }
74770
74771 ma_atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime);
74772
74773 return MA_SUCCESS;
74774}
74775
74776
74777
74778static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
74779{
74780 ma_node_base* pNodeBase = (ma_node_base*)pNode;
74781
74782 MA_ASSERT(pNode != NULL);
74783
74784 if (pNodeBase->vtable->onProcess) {
74785 pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
74786 }
74787}
74788
74789static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime)
74790{
74791 ma_node_base* pNodeBase = (ma_node_base*)pNode;
74792 ma_result result = MA_SUCCESS;
74793 ma_uint32 iInputBus;
74794 ma_uint32 iOutputBus;
74795 ma_uint32 inputBusCount;
74796 ma_uint32 outputBusCount;
74797 ma_uint32 totalFramesRead = 0;
74798 float* ppFramesIn[MA_MAX_NODE_BUS_COUNT];
74799 float* ppFramesOut[MA_MAX_NODE_BUS_COUNT];
74800 ma_uint64 globalTimeBeg;
74801 ma_uint64 globalTimeEnd;
74802 ma_uint64 startTime;
74803 ma_uint64 stopTime;
74804 ma_uint32 timeOffsetBeg;
74805 ma_uint32 timeOffsetEnd;
74806 ma_uint32 frameCountIn;
74807 ma_uint32 frameCountOut;
74808
74809 /*
74810 pFramesRead is mandatory. It must be used to determine how many frames were read. It's normal and
74811 expected that the number of frames read may be different to that requested. Therefore, the caller
74812 must look at this value to correctly determine how many frames were read.
74813 */
74814 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. */
74815 if (pFramesRead == NULL) {
74816 return MA_INVALID_ARGS;
74817 }
74818
74819 *pFramesRead = 0; /* Safety. */
74820
74821 if (pNodeBase == NULL) {
74822 return MA_INVALID_ARGS;
74823 }
74824
74825 if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) {
74826 return MA_INVALID_ARGS; /* Invalid output bus index. */
74827 }
74828
74829 /* Don't do anything if we're in a stopped state. */
74830 if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) {
74831 return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */
74832 }
74833
74834
74835 globalTimeBeg = globalTime;
74836 globalTimeEnd = globalTime + frameCount;
74839
74840 /*
74841 At this point we know that we are inside our start/stop times. However, we may need to adjust
74842 our frame count and output pointer to accommodate since we could be straddling the time period
74843 that this function is getting called for.
74844
74845 It's possible (and likely) that the start time does not line up with the output buffer. We
74846 therefore need to offset it by a number of frames to accommodate. The same thing applies for
74847 the stop time.
74848 */
74849 timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0;
74850 timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0;
74851
74852 /* Trim based on the start offset. We need to silence the start of the buffer. */
74853 if (timeOffsetBeg > 0) {
74854 ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex));
74855 pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex);
74856 frameCount -= timeOffsetBeg;
74857 }
74858
74859 /* 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. */
74860 if (timeOffsetEnd > 0) {
74861 frameCount -= timeOffsetEnd;
74862 }
74863
74864
74865 /* We run on different paths depending on the bus counts. */
74866 inputBusCount = ma_node_get_input_bus_count(pNode);
74867 outputBusCount = ma_node_get_output_bus_count(pNode);
74868
74869 /*
74870 Run a simplified path when there are no inputs and one output. In this case there's nothing to
74871 actually read and we can go straight to output. This is a very common scenario because the vast
74872 majority of data source nodes will use this setup so this optimization I think is worthwhile.
74873 */
74874 if (inputBusCount == 0 && outputBusCount == 1) {
74875 /* Fast path. No need to read from input and no need for any caching. */
74876 frameCountIn = 0;
74877 frameCountOut = frameCount; /* Just read as much as we can. The callback will return what was actually read. */
74878
74879 ppFramesOut[0] = pFramesOut;
74880
74881 /*
74882 If it's a passthrough we won't be expecting the callback to output anything, so we'll
74883 need to pre-silence the output buffer.
74884 */
74885 if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {
74886 ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex));
74887 }
74888
74889 ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut);
74890 totalFramesRead = frameCountOut;
74891 } else {
74892 /* Slow path. Need to read input data. */
74893 if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {
74894 /*
74895 Fast path. We're running a passthrough. We need to read directly into the output buffer, but
74896 still fire the callback so that event handling and trigger nodes can do their thing. Since
74897 it's a passthrough there's no need for any kind of caching logic.
74898 */
74899 MA_ASSERT(outputBusCount == inputBusCount);
74900 MA_ASSERT(outputBusCount == 1);
74901 MA_ASSERT(outputBusIndex == 0);
74902
74903 /* We just read directly from input bus to output buffer, and then afterwards fire the callback. */
74904 ppFramesOut[0] = pFramesOut;
74905 ppFramesIn[0] = ppFramesOut[0];
74906
74907 result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime);
74908 if (result == MA_SUCCESS) {
74909 /* Even though it's a passthrough, we still need to fire the callback. */
74910 frameCountIn = totalFramesRead;
74911 frameCountOut = totalFramesRead;
74912
74913 if (totalFramesRead > 0) {
74914 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? Explicit cast to silence the warning. */
74915 }
74916
74917 /*
74918 A passthrough should never have modified the input and output frame counts. If you're
74919 triggering these asserts you need to fix your processing callback.
74920 */
74921 MA_ASSERT(frameCountIn == totalFramesRead);
74922 MA_ASSERT(frameCountOut == totalFramesRead);
74923 }
74924 } else {
74925 /* Slow path. Need to do caching. */
74926 ma_uint32 framesToProcessIn;
74927 ma_uint32 framesToProcessOut;
74928 ma_bool32 consumeNullInput = MA_FALSE;
74929
74930 /*
74931 We use frameCount as a basis for the number of frames to read since that's what's being
74932 requested, however we still need to clamp it to whatever can fit in the cache.
74933
74934 This will also be used as the basis for determining how many input frames to read. This is
74935 not ideal because it can result in too many input frames being read which introduces latency.
74936 To solve this, nodes can implement an optional callback called onGetRequiredInputFrameCount
74937 which is used as hint to miniaudio as to how many input frames it needs to read at a time. This
74938 callback is completely optional, and if it's not set, miniaudio will assume `frameCount`.
74939
74940 This function will be called multiple times for each period of time, once for each output node.
74941 We cannot read from each input node each time this function is called. Instead we need to check
74942 whether or not this is first output bus to be read from for this time period, and if so, read
74943 from our input data.
74944
74945 To determine whether or not we're ready to read data, we check a flag. There will be one flag
74946 for each output. When the flag is set, it means data has been read previously and that we're
74947 ready to advance time forward for our input nodes by reading fresh data.
74948 */
74949 framesToProcessOut = frameCount;
74950 if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) {
74951 framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus;
74952 }
74953
74954 framesToProcessIn = frameCount;
74955 if (pNodeBase->vtable->onGetRequiredInputFrameCount) {
74956 pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut, &framesToProcessIn); /* <-- It does not matter if this fails. */
74957 }
74958 if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) {
74959 framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus;
74960 }
74961
74962
74963 MA_ASSERT(framesToProcessIn <= 0xFFFF);
74964 MA_ASSERT(framesToProcessOut <= 0xFFFF);
74965
74966 if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) {
74967 /* Getting here means we need to do another round of processing. */
74968 pNodeBase->cachedFrameCountOut = 0;
74969
74970 for (;;) {
74971 frameCountOut = 0;
74972
74973 /*
74974 We need to prepare our output frame pointers for processing. In the same iteration we need
74975 to mark every output bus as unread so that future calls to this function for different buses
74976 for the current time period don't pull in data when they should instead be reading from cache.
74977 */
74978 for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) {
74979 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. */
74980 ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus);
74981 }
74982
74983 /* We only need to read from input buses if there isn't already some data in the cache. */
74984 if (pNodeBase->cachedFrameCountIn == 0) {
74985 ma_uint32 maxFramesReadIn = 0;
74986
74987 /* Here is where we pull in data from the input buses. This is what will trigger an advance in time. */
74988 for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) {
74989 ma_uint32 framesRead;
74990
74991 /* The first thing to do is get the offset within our bulk allocation to store this input data. */
74992 ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus);
74993
74994 /* 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. */
74995 result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime);
74996 if (result != MA_SUCCESS) {
74997 /* It doesn't really matter if we fail because we'll just fill with silence. */
74998 framesRead = 0; /* Just for safety, but I don't think it's really needed. */
74999 }
75000
75001 /* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */
75002 /* Any leftover frames need to silenced for safety. */
75003 if (framesRead < framesToProcessIn) {
75004 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));
75005 }
75006
75007 maxFramesReadIn = ma_max(maxFramesReadIn, framesRead);
75008 }
75009
75010 /* This was a fresh load of input data so reset our consumption counter. */
75011 pNodeBase->consumedFrameCountIn = 0;
75012
75013 /*
75014 We don't want to keep processing if there's nothing to process, so set the number of cached
75015 input frames to the maximum number we read from each attachment (the lesser will be padded
75016 with silence). If we didn't read anything, this will be set to 0 and the entire buffer will
75017 have been assigned to silence. This being equal to 0 is an important property for us because
75018 it allows us to detect when NULL can be passed into the processing callback for the input
75019 buffer for the purpose of continuous processing.
75020 */
75021 pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn;
75022 } else {
75023 /* We don't need to read anything, but we do need to prepare our input frame pointers. */
75024 for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) {
75025 ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus));
75026 }
75027 }
75028
75029 /*
75030 At this point we have our input data so now we need to do some processing. Sneaky little
75031 optimization here - we can set the pointer to the output buffer for this output bus so
75032 that the final copy into the output buffer is done directly by onProcess().
75033 */
75034 if (pFramesOut != NULL) {
75035 ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex));
75036 }
75037
75038
75039 /* Give the processing function the entire capacity of the output buffer. */
75040 frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut);
75041
75042 /*
75043 We need to treat nodes with continuous processing a little differently. For these ones,
75044 we always want to fire the callback with the requested number of frames, regardless of
75045 pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass
75046 in NULL for the input buffer to the callback.
75047 */
75048 if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) {
75049 /* We're using continuous processing. Make sure we specify the whole frame count at all times. */
75050 frameCountIn = framesToProcessIn; /* Give the processing function as much input data as we've got in the buffer, including any silenced padding from short reads. */
75051
75052 if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) {
75053 consumeNullInput = MA_TRUE;
75054 } else {
75055 consumeNullInput = MA_FALSE;
75056 }
75057
75058 /*
75059 Since we're using continuous processing we're always passing in a full frame count
75060 regardless of how much input data was read. If this is greater than what we read as
75061 input, we'll end up with an underflow. We instead need to make sure our cached frame
75062 count is set to the number of frames we'll be passing to the data callback. Not
75063 doing this will result in an underflow when we "consume" the cached data later on.
75064
75065 Note that this check needs to be done after the "consumeNullInput" check above because
75066 we use the property of cachedFrameCountIn being 0 to determine whether or not we
75067 should be passing in a null pointer to the processing callback for when the node is
75068 configured with MA_NODE_FLAG_ALLOW_NULL_INPUT.
75069 */
75070 if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) {
75071 pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn;
75072 }
75073 } else {
75074 frameCountIn = pNodeBase->cachedFrameCountIn; /* Give the processing function as much valid input data as we've got. */
75075 consumeNullInput = MA_FALSE;
75076 }
75077
75078 /*
75079 Process data slightly differently depending on whether or not we're consuming NULL
75080 input (checked just above).
75081 */
75082 if (consumeNullInput) {
75083 ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut);
75084 } else {
75085 /*
75086 We want to skip processing if there's no input data, but we can only do that safely if
75087 we know that there is no chance of any output frames being produced. If continuous
75088 processing is being used, this won't be a problem because the input frame count will
75089 always be non-0. However, if continuous processing is *not* enabled and input and output
75090 data is processed at different rates, we still need to process that last input frame
75091 because there could be a few excess output frames needing to be produced from cached
75092 data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for
75093 determining whether or not we need to process the node even when there are no input
75094 frames available right now.
75095 */
75096 if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) {
75097 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? Explicit cast to silence the warning. */
75098 } else {
75099 frameCountOut = 0; /* No data was processed. */
75100 }
75101 }
75102
75103 /*
75104 Thanks to our sneaky optimization above we don't need to do any data copying directly into
75105 the output buffer - the onProcess() callback just did that for us. We do, however, need to
75106 apply the number of input and output frames that were processed. Note that due to continuous
75107 processing above, we need to do explicit checks here. If we just consumed a NULL input
75108 buffer it means that no actual input data was processed from the internal buffers and we
75109 don't want to be modifying any counters.
75110 */
75111 if (consumeNullInput == MA_FALSE) {
75112 pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn;
75113 pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn;
75114 }
75115
75116 /* The cached output frame count is always equal to what we just read. */
75117 pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut;
75118
75119 /* 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. */
75120 if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) {
75121 break;
75122 }
75123 }
75124 } else {
75125 /*
75126 We're not needing to read anything from the input buffer so just read directly from our
75127 already-processed data.
75128 */
75129 if (pFramesOut != NULL) {
75130 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));
75131 }
75132 }
75133
75134 /* The number of frames read is always equal to the number of cached output frames. */
75135 totalFramesRead = pNodeBase->cachedFrameCountOut;
75136
75137 /* Now that we've read the data, make sure our read flag is set. */
75138 ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE);
75139 }
75140 }
75141
75142 /* Apply volume, if necessary. */
75143 ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]));
75144
75145 /* Advance our local time forward. */
75146 ma_atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead);
75147
75148 *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */
75149 return result;
75150}
75151
75152
75153
75154
75155/* Data source node. */
75156MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource)
75157{
75158 ma_data_source_node_config config;
75159
75160 MA_ZERO_OBJECT(&config);
75162 config.pDataSource = pDataSource;
75163
75164 return config;
75165}
75166
75167
75168static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
75169{
75170 ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode;
75171 ma_format format;
75172 ma_uint32 channels;
75173 ma_uint32 frameCount;
75174 ma_uint64 framesRead = 0;
75175
75176 MA_ASSERT(pDataSourceNode != NULL);
75177 MA_ASSERT(pDataSourceNode->pDataSource != NULL);
75178 MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode) == 0);
75179 MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1);
75180
75181 /* We don't want to read from ppFramesIn at all. Instead we read from the data source. */
75182 (void)ppFramesIn;
75183 (void)pFrameCountIn;
75184
75185 frameCount = *pFrameCountOut;
75186
75187 /* miniaudio should never be calling this with a frame count of zero. */
75188 MA_ASSERT(frameCount > 0);
75189
75190 if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL, NULL, 0) == MA_SUCCESS) { /* <-- Don't care about sample rate here. */
75191 /* The node graph system requires samples be in floating point format. This is checked in ma_data_source_node_init(). */
75192 MA_ASSERT(format == ma_format_f32);
75193 (void)format; /* Just to silence some static analysis tools. */
75194
75195 ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead);
75196 }
75197
75198 *pFrameCountOut = (ma_uint32)framesRead;
75199}
75200
75201static ma_node_vtable g_ma_data_source_node_vtable =
75202{
75203 ma_data_source_node_process_pcm_frames,
75204 NULL, /* onGetRequiredInputFrameCount */
75205 0, /* 0 input buses. */
75206 1, /* 1 output bus. */
75207 0
75208};
75209
75210MA_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)
75211{
75212 ma_result result;
75213 ma_format format; /* For validating the format, which must be ma_format_f32. */
75214 ma_uint32 channels; /* For specifying the channel count of the output bus. */
75215 ma_node_config baseConfig;
75216
75217 if (pDataSourceNode == NULL) {
75218 return MA_INVALID_ARGS;
75219 }
75220
75221 MA_ZERO_OBJECT(pDataSourceNode);
75222
75223 if (pConfig == NULL) {
75224 return MA_INVALID_ARGS;
75225 }
75226
75227 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. */
75228 if (result != MA_SUCCESS) {
75229 return result;
75230 }
75231
75232 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. */
75233 if (format != ma_format_f32) {
75234 return MA_INVALID_ARGS; /* Invalid format. */
75235 }
75236
75237 /* The channel count is defined by the data source. If the caller has manually changed the channels we just ignore it. */
75238 baseConfig = pConfig->nodeConfig;
75239 baseConfig.vtable = &g_ma_data_source_node_vtable; /* Explicitly set the vtable here to prevent callers from setting it incorrectly. */
75240
75241 /*
75242 The channel count is defined by the data source. It is invalid for the caller to manually set
75243 the channel counts in the config. `ma_data_source_node_config_init()` will have defaulted the
75244 channel count pointer to NULL which is how it must remain. If you trigger any of these asserts
75245 it means you're explicitly setting the channel count. Instead, configure the output channel
75246 count of your data source to be the necessary channel count.
75247 */
75248 if (baseConfig.pOutputChannels != NULL) {
75249 return MA_INVALID_ARGS;
75250 }
75251
75252 baseConfig.pOutputChannels = &channels;
75253
75254 result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base);
75255 if (result != MA_SUCCESS) {
75256 return result;
75257 }
75258
75259 pDataSourceNode->pDataSource = pConfig->pDataSource;
75260
75261 return MA_SUCCESS;
75262}
75263
75264MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks)
75265{
75266 ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks);
75267}
75268
75269MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping)
75270{
75271 if (pDataSourceNode == NULL) {
75272 return MA_INVALID_ARGS;
75273 }
75274
75275 return ma_data_source_set_looping(pDataSourceNode->pDataSource, isLooping);
75276}
75277
75278MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode)
75279{
75280 if (pDataSourceNode == NULL) {
75281 return MA_FALSE;
75282 }
75283
75284 return ma_data_source_is_looping(pDataSourceNode->pDataSource);
75285}
75286
75287
75288
75289/* Splitter Node. */
75290MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels)
75291{
75292 ma_splitter_node_config config;
75293
75294 MA_ZERO_OBJECT(&config);
75296 config.channels = channels;
75297 config.outputBusCount = 2;
75298
75299 return config;
75300}
75301
75302
75303static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
75304{
75305 ma_node_base* pNodeBase = (ma_node_base*)pNode;
75306 ma_uint32 iOutputBus;
75307 ma_uint32 channels;
75308
75309 MA_ASSERT(pNodeBase != NULL);
75310 MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1);
75311
75312 /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */
75313 (void)pFrameCountIn;
75314
75315 /* NOTE: This assumes the same number of channels for all inputs and outputs. This was checked in ma_splitter_node_init(). */
75316 channels = ma_node_get_input_channels(pNodeBase, 0);
75317
75318 /* Splitting is just copying the first input bus and copying it over to each output bus. */
75319 for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) {
75320 ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels);
75321 }
75322}
75323
75324static ma_node_vtable g_ma_splitter_node_vtable =
75325{
75326 ma_splitter_node_process_pcm_frames,
75327 NULL, /* onGetRequiredInputFrameCount */
75328 1, /* 1 input bus. */
75329 MA_NODE_BUS_COUNT_UNKNOWN, /* The output bus count is specified on a per-node basis. */
75330 0
75331};
75332
75333MA_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)
75334{
75335 ma_result result;
75336 ma_node_config baseConfig;
75337 ma_uint32 pInputChannels[1];
75338 ma_uint32 pOutputChannels[MA_MAX_NODE_BUS_COUNT];
75339 ma_uint32 iOutputBus;
75340
75341 if (pSplitterNode == NULL) {
75342 return MA_INVALID_ARGS;
75343 }
75344
75345 MA_ZERO_OBJECT(pSplitterNode);
75346
75347 if (pConfig == NULL) {
75348 return MA_INVALID_ARGS;
75349 }
75350
75351 if (pConfig->outputBusCount > MA_MAX_NODE_BUS_COUNT) {
75352 return MA_INVALID_ARGS; /* Too many output buses. */
75353 }
75354
75355 /* Splitters require the same number of channels between inputs and outputs. */
75356 pInputChannels[0] = pConfig->channels;
75357 for (iOutputBus = 0; iOutputBus < pConfig->outputBusCount; iOutputBus += 1) {
75358 pOutputChannels[iOutputBus] = pConfig->channels;
75359 }
75360
75361 baseConfig = pConfig->nodeConfig;
75362 baseConfig.vtable = &g_ma_splitter_node_vtable;
75363 baseConfig.pInputChannels = pInputChannels;
75364 baseConfig.pOutputChannels = pOutputChannels;
75365 baseConfig.outputBusCount = pConfig->outputBusCount;
75366
75367 result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base);
75368 if (result != MA_SUCCESS) {
75369 return result; /* Failed to initialize the base node. */
75370 }
75371
75372 return MA_SUCCESS;
75373}
75374
75375MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks)
75376{
75377 ma_node_uninit(pSplitterNode, pAllocationCallbacks);
75378}
75379
75380
75381/*
75382Biquad Node
75383*/
75384MA_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)
75385{
75386 ma_biquad_node_config config;
75387
75389 config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);
75390
75391 return config;
75392}
75393
75394static void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
75395{
75396 ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
75397
75398 MA_ASSERT(pNode != NULL);
75399 (void)pFrameCountIn;
75400
75401 ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
75402}
75403
75404static ma_node_vtable g_ma_biquad_node_vtable =
75405{
75406 ma_biquad_node_process_pcm_frames,
75407 NULL, /* onGetRequiredInputFrameCount */
75408 1, /* One input. */
75409 1, /* One output. */
75410 0 /* Default flags. */
75411};
75412
75413MA_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)
75414{
75415 ma_result result;
75416 ma_node_config baseNodeConfig;
75417
75418 if (pNode == NULL) {
75419 return MA_INVALID_ARGS;
75420 }
75421
75422 MA_ZERO_OBJECT(pNode);
75423
75424 if (pConfig == NULL) {
75425 return MA_INVALID_ARGS;
75426 }
75427
75428 if (pConfig->biquad.format != ma_format_f32) {
75429 return MA_INVALID_ARGS; /* The format must be f32. */
75430 }
75431
75432 result = ma_biquad_init(&pConfig->biquad, pAllocationCallbacks, &pNode->biquad);
75433 if (result != MA_SUCCESS) {
75434 return result;
75435 }
75436
75437 baseNodeConfig = ma_node_config_init();
75438 baseNodeConfig.vtable = &g_ma_biquad_node_vtable;
75439 baseNodeConfig.pInputChannels = &pConfig->biquad.channels;
75440 baseNodeConfig.pOutputChannels = &pConfig->biquad.channels;
75441
75442 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
75443 if (result != MA_SUCCESS) {
75444 return result;
75445 }
75446
75447 return result;
75448}
75449
75450MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode)
75451{
75452 ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
75453
75454 MA_ASSERT(pNode != NULL);
75455
75456 return ma_biquad_reinit(pConfig, &pLPFNode->biquad);
75457}
75458
75459MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
75460{
75461 ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
75462
75463 if (pNode == NULL) {
75464 return;
75465 }
75466
75467 ma_node_uninit(pNode, pAllocationCallbacks);
75468 ma_biquad_uninit(&pLPFNode->biquad, pAllocationCallbacks);
75469}
75470
75471
75472
75473/*
75474Low Pass Filter Node
75475*/
75476MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
75477{
75478 ma_lpf_node_config config;
75479
75481 config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
75482
75483 return config;
75484}
75485
75486static void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
75487{
75488 ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
75489
75490 MA_ASSERT(pNode != NULL);
75491 (void)pFrameCountIn;
75492
75493 ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
75494}
75495
75496static ma_node_vtable g_ma_lpf_node_vtable =
75497{
75498 ma_lpf_node_process_pcm_frames,
75499 NULL, /* onGetRequiredInputFrameCount */
75500 1, /* One input. */
75501 1, /* One output. */
75502 0 /* Default flags. */
75503};
75504
75505MA_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)
75506{
75507 ma_result result;
75508 ma_node_config baseNodeConfig;
75509
75510 if (pNode == NULL) {
75511 return MA_INVALID_ARGS;
75512 }
75513
75514 MA_ZERO_OBJECT(pNode);
75515
75516 if (pConfig == NULL) {
75517 return MA_INVALID_ARGS;
75518 }
75519
75520 if (pConfig->lpf.format != ma_format_f32) {
75521 return MA_INVALID_ARGS; /* The format must be f32. */
75522 }
75523
75524 result = ma_lpf_init(&pConfig->lpf, pAllocationCallbacks, &pNode->lpf);
75525 if (result != MA_SUCCESS) {
75526 return result;
75527 }
75528
75529 baseNodeConfig = ma_node_config_init();
75530 baseNodeConfig.vtable = &g_ma_lpf_node_vtable;
75531 baseNodeConfig.pInputChannels = &pConfig->lpf.channels;
75532 baseNodeConfig.pOutputChannels = &pConfig->lpf.channels;
75533
75534 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
75535 if (result != MA_SUCCESS) {
75536 return result;
75537 }
75538
75539 return result;
75540}
75541
75542MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode)
75543{
75544 ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
75545
75546 if (pNode == NULL) {
75547 return MA_INVALID_ARGS;
75548 }
75549
75550 return ma_lpf_reinit(pConfig, &pLPFNode->lpf);
75551}
75552
75553MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
75554{
75555 ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
75556
75557 if (pNode == NULL) {
75558 return;
75559 }
75560
75561 ma_node_uninit(pNode, pAllocationCallbacks);
75562 ma_lpf_uninit(&pLPFNode->lpf, pAllocationCallbacks);
75563}
75564
75565
75566
75567/*
75568High Pass Filter Node
75569*/
75570MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
75571{
75572 ma_hpf_node_config config;
75573
75575 config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
75576
75577 return config;
75578}
75579
75580static void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
75581{
75582 ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
75583
75584 MA_ASSERT(pNode != NULL);
75585 (void)pFrameCountIn;
75586
75587 ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
75588}
75589
75590static ma_node_vtable g_ma_hpf_node_vtable =
75591{
75592 ma_hpf_node_process_pcm_frames,
75593 NULL, /* onGetRequiredInputFrameCount */
75594 1, /* One input. */
75595 1, /* One output. */
75596 0 /* Default flags. */
75597};
75598
75599MA_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)
75600{
75601 ma_result result;
75602 ma_node_config baseNodeConfig;
75603
75604 if (pNode == NULL) {
75605 return MA_INVALID_ARGS;
75606 }
75607
75608 MA_ZERO_OBJECT(pNode);
75609
75610 if (pConfig == NULL) {
75611 return MA_INVALID_ARGS;
75612 }
75613
75614 if (pConfig->hpf.format != ma_format_f32) {
75615 return MA_INVALID_ARGS; /* The format must be f32. */
75616 }
75617
75618 result = ma_hpf_init(&pConfig->hpf, pAllocationCallbacks, &pNode->hpf);
75619 if (result != MA_SUCCESS) {
75620 return result;
75621 }
75622
75623 baseNodeConfig = ma_node_config_init();
75624 baseNodeConfig.vtable = &g_ma_hpf_node_vtable;
75625 baseNodeConfig.pInputChannels = &pConfig->hpf.channels;
75626 baseNodeConfig.pOutputChannels = &pConfig->hpf.channels;
75627
75628 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
75629 if (result != MA_SUCCESS) {
75630 return result;
75631 }
75632
75633 return result;
75634}
75635
75636MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode)
75637{
75638 ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
75639
75640 if (pNode == NULL) {
75641 return MA_INVALID_ARGS;
75642 }
75643
75644 return ma_hpf_reinit(pConfig, &pHPFNode->hpf);
75645}
75646
75647MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
75648{
75649 ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
75650
75651 if (pNode == NULL) {
75652 return;
75653 }
75654
75655 ma_node_uninit(pNode, pAllocationCallbacks);
75656 ma_hpf_uninit(&pHPFNode->hpf, pAllocationCallbacks);
75657}
75658
75659
75660
75661
75662/*
75663Band Pass Filter Node
75664*/
75665MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
75666{
75667 ma_bpf_node_config config;
75668
75670 config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
75671
75672 return config;
75673}
75674
75675static void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
75676{
75677 ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
75678
75679 MA_ASSERT(pNode != NULL);
75680 (void)pFrameCountIn;
75681
75682 ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
75683}
75684
75685static ma_node_vtable g_ma_bpf_node_vtable =
75686{
75687 ma_bpf_node_process_pcm_frames,
75688 NULL, /* onGetRequiredInputFrameCount */
75689 1, /* One input. */
75690 1, /* One output. */
75691 0 /* Default flags. */
75692};
75693
75694MA_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)
75695{
75696 ma_result result;
75697 ma_node_config baseNodeConfig;
75698
75699 if (pNode == NULL) {
75700 return MA_INVALID_ARGS;
75701 }
75702
75703 MA_ZERO_OBJECT(pNode);
75704
75705 if (pConfig == NULL) {
75706 return MA_INVALID_ARGS;
75707 }
75708
75709 if (pConfig->bpf.format != ma_format_f32) {
75710 return MA_INVALID_ARGS; /* The format must be f32. */
75711 }
75712
75713 result = ma_bpf_init(&pConfig->bpf, pAllocationCallbacks, &pNode->bpf);
75714 if (result != MA_SUCCESS) {
75715 return result;
75716 }
75717
75718 baseNodeConfig = ma_node_config_init();
75719 baseNodeConfig.vtable = &g_ma_bpf_node_vtable;
75720 baseNodeConfig.pInputChannels = &pConfig->bpf.channels;
75721 baseNodeConfig.pOutputChannels = &pConfig->bpf.channels;
75722
75723 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
75724 if (result != MA_SUCCESS) {
75725 return result;
75726 }
75727
75728 return result;
75729}
75730
75731MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode)
75732{
75733 ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
75734
75735 if (pNode == NULL) {
75736 return MA_INVALID_ARGS;
75737 }
75738
75739 return ma_bpf_reinit(pConfig, &pBPFNode->bpf);
75740}
75741
75742MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
75743{
75744 ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
75745
75746 if (pNode == NULL) {
75747 return;
75748 }
75749
75750 ma_node_uninit(pNode, pAllocationCallbacks);
75751 ma_bpf_uninit(&pBPFNode->bpf, pAllocationCallbacks);
75752}
75753
75754
75755
75756/*
75757Notching Filter Node
75758*/
75759MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
75760{
75761 ma_notch_node_config config;
75762
75764 config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency);
75765
75766 return config;
75767}
75768
75769static void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
75770{
75771 ma_notch_node* pBPFNode = (ma_notch_node*)pNode;
75772
75773 MA_ASSERT(pNode != NULL);
75774 (void)pFrameCountIn;
75775
75776 ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
75777}
75778
75779static ma_node_vtable g_ma_notch_node_vtable =
75780{
75781 ma_notch_node_process_pcm_frames,
75782 NULL, /* onGetRequiredInputFrameCount */
75783 1, /* One input. */
75784 1, /* One output. */
75785 0 /* Default flags. */
75786};
75787
75788MA_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)
75789{
75790 ma_result result;
75791 ma_node_config baseNodeConfig;
75792
75793 if (pNode == NULL) {
75794 return MA_INVALID_ARGS;
75795 }
75796
75797 MA_ZERO_OBJECT(pNode);
75798
75799 if (pConfig == NULL) {
75800 return MA_INVALID_ARGS;
75801 }
75802
75803 if (pConfig->notch.format != ma_format_f32) {
75804 return MA_INVALID_ARGS; /* The format must be f32. */
75805 }
75806
75807 result = ma_notch2_init(&pConfig->notch, pAllocationCallbacks, &pNode->notch);
75808 if (result != MA_SUCCESS) {
75809 return result;
75810 }
75811
75812 baseNodeConfig = ma_node_config_init();
75813 baseNodeConfig.vtable = &g_ma_notch_node_vtable;
75814 baseNodeConfig.pInputChannels = &pConfig->notch.channels;
75815 baseNodeConfig.pOutputChannels = &pConfig->notch.channels;
75816
75817 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
75818 if (result != MA_SUCCESS) {
75819 return result;
75820 }
75821
75822 return result;
75823}
75824
75825MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode)
75826{
75827 ma_notch_node* pNotchNode = (ma_notch_node*)pNode;
75828
75829 if (pNode == NULL) {
75830 return MA_INVALID_ARGS;
75831 }
75832
75833 return ma_notch2_reinit(pConfig, &pNotchNode->notch);
75834}
75835
75836MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
75837{
75838 ma_notch_node* pNotchNode = (ma_notch_node*)pNode;
75839
75840 if (pNode == NULL) {
75841 return;
75842 }
75843
75844 ma_node_uninit(pNode, pAllocationCallbacks);
75845 ma_notch2_uninit(&pNotchNode->notch, pAllocationCallbacks);
75846}
75847
75848
75849
75850/*
75851Peaking Filter Node
75852*/
75853MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
75854{
75855 ma_peak_node_config config;
75856
75858 config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
75859
75860 return config;
75861}
75862
75863static void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
75864{
75865 ma_peak_node* pBPFNode = (ma_peak_node*)pNode;
75866
75867 MA_ASSERT(pNode != NULL);
75868 (void)pFrameCountIn;
75869
75870 ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
75871}
75872
75873static ma_node_vtable g_ma_peak_node_vtable =
75874{
75875 ma_peak_node_process_pcm_frames,
75876 NULL, /* onGetRequiredInputFrameCount */
75877 1, /* One input. */
75878 1, /* One output. */
75879 0 /* Default flags. */
75880};
75881
75882MA_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)
75883{
75884 ma_result result;
75885 ma_node_config baseNodeConfig;
75886
75887 if (pNode == NULL) {
75888 return MA_INVALID_ARGS;
75889 }
75890
75891 MA_ZERO_OBJECT(pNode);
75892
75893 if (pConfig == NULL) {
75894 return MA_INVALID_ARGS;
75895 }
75896
75897 if (pConfig->peak.format != ma_format_f32) {
75898 return MA_INVALID_ARGS; /* The format must be f32. */
75899 }
75900
75901 result = ma_peak2_init(&pConfig->peak, pAllocationCallbacks, &pNode->peak);
75902 if (result != MA_SUCCESS) {
75903 ma_node_uninit(pNode, pAllocationCallbacks);
75904 return result;
75905 }
75906
75907 baseNodeConfig = ma_node_config_init();
75908 baseNodeConfig.vtable = &g_ma_peak_node_vtable;
75909 baseNodeConfig.pInputChannels = &pConfig->peak.channels;
75910 baseNodeConfig.pOutputChannels = &pConfig->peak.channels;
75911
75912 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
75913 if (result != MA_SUCCESS) {
75914 return result;
75915 }
75916
75917 return result;
75918}
75919
75920MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode)
75921{
75922 ma_peak_node* pPeakNode = (ma_peak_node*)pNode;
75923
75924 if (pNode == NULL) {
75925 return MA_INVALID_ARGS;
75926 }
75927
75928 return ma_peak2_reinit(pConfig, &pPeakNode->peak);
75929}
75930
75931MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
75932{
75933 ma_peak_node* pPeakNode = (ma_peak_node*)pNode;
75934
75935 if (pNode == NULL) {
75936 return;
75937 }
75938
75939 ma_node_uninit(pNode, pAllocationCallbacks);
75940 ma_peak2_uninit(&pPeakNode->peak, pAllocationCallbacks);
75941}
75942
75943
75944
75945/*
75946Low Shelf Filter Node
75947*/
75948MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
75949{
75950 ma_loshelf_node_config config;
75951
75953 config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
75954
75955 return config;
75956}
75957
75958static void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
75959{
75960 ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode;
75961
75962 MA_ASSERT(pNode != NULL);
75963 (void)pFrameCountIn;
75964
75965 ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
75966}
75967
75968static ma_node_vtable g_ma_loshelf_node_vtable =
75969{
75970 ma_loshelf_node_process_pcm_frames,
75971 NULL, /* onGetRequiredInputFrameCount */
75972 1, /* One input. */
75973 1, /* One output. */
75974 0 /* Default flags. */
75975};
75976
75977MA_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)
75978{
75979 ma_result result;
75980 ma_node_config baseNodeConfig;
75981
75982 if (pNode == NULL) {
75983 return MA_INVALID_ARGS;
75984 }
75985
75986 MA_ZERO_OBJECT(pNode);
75987
75988 if (pConfig == NULL) {
75989 return MA_INVALID_ARGS;
75990 }
75991
75992 if (pConfig->loshelf.format != ma_format_f32) {
75993 return MA_INVALID_ARGS; /* The format must be f32. */
75994 }
75995
75996 result = ma_loshelf2_init(&pConfig->loshelf, pAllocationCallbacks, &pNode->loshelf);
75997 if (result != MA_SUCCESS) {
75998 return result;
75999 }
76000
76001 baseNodeConfig = ma_node_config_init();
76002 baseNodeConfig.vtable = &g_ma_loshelf_node_vtable;
76003 baseNodeConfig.pInputChannels = &pConfig->loshelf.channels;
76004 baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels;
76005
76006 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
76007 if (result != MA_SUCCESS) {
76008 return result;
76009 }
76010
76011 return result;
76012}
76013
76014MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode)
76015{
76016 ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode;
76017
76018 if (pNode == NULL) {
76019 return MA_INVALID_ARGS;
76020 }
76021
76022 return ma_loshelf2_reinit(pConfig, &pLoshelfNode->loshelf);
76023}
76024
76025MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
76026{
76027 ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode;
76028
76029 if (pNode == NULL) {
76030 return;
76031 }
76032
76033 ma_node_uninit(pNode, pAllocationCallbacks);
76034 ma_loshelf2_uninit(&pLoshelfNode->loshelf, pAllocationCallbacks);
76035}
76036
76037
76038
76039/*
76040High Shelf Filter Node
76041*/
76042MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
76043{
76044 ma_hishelf_node_config config;
76045
76047 config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
76048
76049 return config;
76050}
76051
76052static void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
76053{
76054 ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode;
76055
76056 MA_ASSERT(pNode != NULL);
76057 (void)pFrameCountIn;
76058
76059 ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
76060}
76061
76062static ma_node_vtable g_ma_hishelf_node_vtable =
76063{
76064 ma_hishelf_node_process_pcm_frames,
76065 NULL, /* onGetRequiredInputFrameCount */
76066 1, /* One input. */
76067 1, /* One output. */
76068 0 /* Default flags. */
76069};
76070
76071MA_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)
76072{
76073 ma_result result;
76074 ma_node_config baseNodeConfig;
76075
76076 if (pNode == NULL) {
76077 return MA_INVALID_ARGS;
76078 }
76079
76080 MA_ZERO_OBJECT(pNode);
76081
76082 if (pConfig == NULL) {
76083 return MA_INVALID_ARGS;
76084 }
76085
76086 if (pConfig->hishelf.format != ma_format_f32) {
76087 return MA_INVALID_ARGS; /* The format must be f32. */
76088 }
76089
76090 result = ma_hishelf2_init(&pConfig->hishelf, pAllocationCallbacks, &pNode->hishelf);
76091 if (result != MA_SUCCESS) {
76092 return result;
76093 }
76094
76095 baseNodeConfig = ma_node_config_init();
76096 baseNodeConfig.vtable = &g_ma_hishelf_node_vtable;
76097 baseNodeConfig.pInputChannels = &pConfig->hishelf.channels;
76098 baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels;
76099
76100 result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
76101 if (result != MA_SUCCESS) {
76102 return result;
76103 }
76104
76105 return result;
76106}
76107
76108MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode)
76109{
76110 ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode;
76111
76112 if (pNode == NULL) {
76113 return MA_INVALID_ARGS;
76114 }
76115
76116 return ma_hishelf2_reinit(pConfig, &pHishelfNode->hishelf);
76117}
76118
76119MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
76120{
76121 ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode;
76122
76123 if (pNode == NULL) {
76124 return;
76125 }
76126
76127 ma_node_uninit(pNode, pAllocationCallbacks);
76128 ma_hishelf2_uninit(&pHishelfNode->hishelf, pAllocationCallbacks);
76129}
76130
76131
76132
76133
76134MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)
76135{
76136 ma_delay_node_config config;
76137
76139 config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay);
76140
76141 return config;
76142}
76143
76144
76145static void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
76146{
76147 ma_delay_node* pDelayNode = (ma_delay_node*)pNode;
76148
76149 (void)pFrameCountIn;
76150
76151 ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
76152}
76153
76154static ma_node_vtable g_ma_delay_node_vtable =
76155{
76156 ma_delay_node_process_pcm_frames,
76157 NULL,
76158 1, /* 1 input channels. */
76159 1, /* 1 output channel. */
76160 MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Delay requires continuous processing to ensure the tail get's processed. */
76161};
76162
76163MA_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)
76164{
76165 ma_result result;
76166 ma_node_config baseConfig;
76167
76168 if (pDelayNode == NULL) {
76169 return MA_INVALID_ARGS;
76170 }
76171
76172 MA_ZERO_OBJECT(pDelayNode);
76173
76174 result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay);
76175 if (result != MA_SUCCESS) {
76176 return result;
76177 }
76178
76179 baseConfig = pConfig->nodeConfig;
76180 baseConfig.vtable = &g_ma_delay_node_vtable;
76181 baseConfig.pInputChannels = &pConfig->delay.channels;
76182 baseConfig.pOutputChannels = &pConfig->delay.channels;
76183
76184 result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode);
76185 if (result != MA_SUCCESS) {
76186 ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks);
76187 return result;
76188 }
76189
76190 return result;
76191}
76192
76193MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks)
76194{
76195 if (pDelayNode == NULL) {
76196 return;
76197 }
76198
76199 /* The base node is always uninitialized first. */
76200 ma_node_uninit(pDelayNode, pAllocationCallbacks);
76201 ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks);
76202}
76203
76204MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value)
76205{
76206 if (pDelayNode == NULL) {
76207 return;
76208 }
76209
76210 ma_delay_set_wet(&pDelayNode->delay, value);
76211}
76212
76213MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode)
76214{
76215 if (pDelayNode == NULL) {
76216 return 0;
76217 }
76218
76219 return ma_delay_get_wet(&pDelayNode->delay);
76220}
76221
76222MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value)
76223{
76224 if (pDelayNode == NULL) {
76225 return;
76226 }
76227
76228 ma_delay_set_dry(&pDelayNode->delay, value);
76229}
76230
76231MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode)
76232{
76233 if (pDelayNode == NULL) {
76234 return 0;
76235 }
76236
76237 return ma_delay_get_dry(&pDelayNode->delay);
76238}
76239
76240MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value)
76241{
76242 if (pDelayNode == NULL) {
76243 return;
76244 }
76245
76246 ma_delay_set_decay(&pDelayNode->delay, value);
76247}
76248
76249MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode)
76250{
76251 if (pDelayNode == NULL) {
76252 return 0;
76253 }
76254
76255 return ma_delay_get_decay(&pDelayNode->delay);
76256}
76257#endif /* MA_NO_NODE_GRAPH */
76258
76259
76260/* SECTION: miniaudio_engine.c */
76261#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH)
76262/**************************************************************************************************************************************************************
76263
76264Engine
76265
76266**************************************************************************************************************************************************************/
76267#define MA_SEEK_TARGET_NONE (~(ma_uint64)0)
76268
76269
76270static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd)
76271{
76272 MA_ASSERT(pSound != NULL);
76273 ma_atomic_exchange_32(&pSound->atEnd, atEnd);
76274
76275 /* Fire any callbacks or events. */
76276 if (atEnd) {
76277 if (pSound->endCallback != NULL) {
76278 pSound->endCallback(pSound->pEndCallbackUserData, pSound);
76279 }
76280 }
76281}
76282
76283static ma_bool32 ma_sound_get_at_end(const ma_sound* pSound)
76284{
76285 MA_ASSERT(pSound != NULL);
76286 return ma_atomic_load_32(&pSound->atEnd);
76287}
76288
76289
76290MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags)
76291{
76292 ma_engine_node_config config;
76293
76294 MA_ZERO_OBJECT(&config);
76295 config.pEngine = pEngine;
76296 config.type = type;
76297 config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0;
76299 config.monoExpansionMode = pEngine->monoExpansionMode;
76300
76301 return config;
76302}
76303
76304
76305static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode)
76306{
76307 ma_bool32 isUpdateRequired = MA_FALSE;
76308 float newPitch;
76309
76310 MA_ASSERT(pEngineNode != NULL);
76311
76312 newPitch = ma_atomic_load_explicit_f32(&pEngineNode->pitch, ma_atomic_memory_order_acquire);
76313
76314 if (pEngineNode->oldPitch != newPitch) {
76315 pEngineNode->oldPitch = newPitch;
76316 isUpdateRequired = MA_TRUE;
76317 }
76318
76319 if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) {
76320 pEngineNode->oldDopplerPitch = pEngineNode->spatializer.dopplerPitch;
76321 isUpdateRequired = MA_TRUE;
76322 }
76323
76324 if (isUpdateRequired) {
76325 float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine);
76326 ma_linear_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch);
76327 }
76328}
76329
76330static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode)
76331{
76332 MA_ASSERT(pEngineNode != NULL);
76333
76334 /* Don't try to be clever by skipping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */
76335 return !ma_atomic_load_explicit_32(&pEngineNode->isPitchDisabled, ma_atomic_memory_order_acquire);
76336}
76337
76338static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode)
76339{
76340 MA_ASSERT(pEngineNode != NULL);
76341
76342 return !ma_atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, ma_atomic_memory_order_acquire);
76343}
76344
76345static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount)
76346{
76347 ma_uint64 inputFrameCount = 0;
76348
76349 if (ma_engine_node_is_pitching_enabled(pEngineNode)) {
76350 ma_result result = ma_linear_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount, &inputFrameCount);
76351 if (result != MA_SUCCESS) {
76352 inputFrameCount = 0;
76353 }
76354 } else {
76355 inputFrameCount = outputFrameCount; /* No resampling, so 1:1. */
76356 }
76357
76358 return inputFrameCount;
76359}
76360
76361static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float volume)
76362{
76363 if (pEngineNode == NULL) {
76364 return MA_INVALID_ARGS;
76365 }
76366
76367 ma_atomic_float_set(&pEngineNode->volume, volume);
76368
76369 /* If we're not smoothing we should bypass the volume gainer entirely. */
76370 if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) {
76371 /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for holding our volume. */
76372 ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume);
76373 } else {
76374 /* We're using volume smoothing, so apply the master volume to the gainer. */
76375 ma_gainer_set_gain(&pEngineNode->volumeGainer, volume);
76376 }
76377
76378 return MA_SUCCESS;
76379}
76380
76381static ma_result ma_engine_node_get_volume(const ma_engine_node* pEngineNode, float* pVolume)
76382{
76383 if (pVolume == NULL) {
76384 return MA_INVALID_ARGS;
76385 }
76386
76387 *pVolume = 0.0f;
76388
76389 if (pEngineNode == NULL) {
76390 return MA_INVALID_ARGS;
76391 }
76392
76393 *pVolume = ma_atomic_float_get((ma_atomic_float*)&pEngineNode->volume);
76394
76395 return MA_SUCCESS;
76396}
76397
76398
76399static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
76400{
76401 ma_uint32 frameCountIn;
76402 ma_uint32 frameCountOut;
76403 ma_uint32 totalFramesProcessedIn;
76404 ma_uint32 totalFramesProcessedOut;
76405 ma_uint32 channelsIn;
76406 ma_uint32 channelsOut;
76407 ma_bool32 isPitchingEnabled;
76408 ma_bool32 isFadingEnabled;
76409 ma_bool32 isSpatializationEnabled;
76410 ma_bool32 isPanningEnabled;
76411 ma_bool32 isVolumeSmoothingEnabled;
76412
76413 frameCountIn = *pFrameCountIn;
76414 frameCountOut = *pFrameCountOut;
76415
76416 channelsIn = ma_spatializer_get_input_channels(&pEngineNode->spatializer);
76417 channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer);
76418
76419 totalFramesProcessedIn = 0;
76420 totalFramesProcessedOut = 0;
76421
76422 /* Update the fader if applicable. */
76423 {
76424 ma_uint64 fadeLengthInFrames = ma_atomic_uint64_get(&pEngineNode->fadeSettings.fadeLengthInFrames);
76425 if (fadeLengthInFrames != ~(ma_uint64)0) {
76426 float fadeVolumeBeg = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeBeg);
76427 float fadeVolumeEnd = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeEnd);
76428 ma_int64 fadeStartOffsetInFrames = (ma_int64)ma_atomic_uint64_get(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames);
76429 if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) {
76430 fadeStartOffsetInFrames = 0;
76431 } else {
76432 fadeStartOffsetInFrames -= ma_engine_get_time_in_pcm_frames(pEngineNode->pEngine);
76433 }
76434
76435 ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames);
76436
76437 /* Reset the fade length so we don't erroneously apply it again. */
76438 ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, ~(ma_uint64)0);
76439 }
76440 }
76441
76442 isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode);
76443 isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1;
76444 isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode);
76445 isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1;
76446 isVolumeSmoothingEnabled = pEngineNode->volumeSmoothTimeInPCMFrames > 0;
76447
76448 /* Keep going while we've still got data available for processing. */
76449 while (totalFramesProcessedOut < frameCountOut) {
76450 /*
76451 We need to process in a specific order. We always do resampling first because it's likely
76452 we're going to be increasing the channel count after spatialization. Also, I want to do
76453 fading based on the output sample rate.
76454
76455 We'll first read into a buffer from the resampler. Then we'll do all processing that
76456 operates on the on the input channel count. We'll then get the spatializer to output to
76457 the output buffer and then do all effects from that point directly in the output buffer
76458 in-place.
76459
76460 Note that we're always running the resampler if pitching is enabled, even when the pitch
76461 is 1. If we try to be clever and skip resampling when the pitch is 1, we'll get a glitch
76462 when we move away from 1, back to 1, and then away from 1 again. We'll want to implement
76463 any pitch=1 optimizations in the resampler itself.
76464
76465 There's a small optimization here that we'll utilize since it might be a fairly common
76466 case. When the input and output channel counts are the same, we'll read straight into the
76467 output buffer from the resampler and do everything in-place.
76468 */
76469 const float* pRunningFramesIn;
76470 float* pRunningFramesOut;
76471 float* pWorkingBuffer; /* This is the buffer that we'll be processing frames in. This is in input channels. */
76472 float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)];
76473 ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn;
76474 ma_uint32 framesAvailableIn;
76475 ma_uint32 framesAvailableOut;
76476 ma_uint32 framesJustProcessedIn;
76477 ma_uint32 framesJustProcessedOut;
76478 ma_bool32 isWorkingBufferValid = MA_FALSE;
76479
76480 framesAvailableIn = frameCountIn - totalFramesProcessedIn;
76481 framesAvailableOut = frameCountOut - totalFramesProcessedOut;
76482
76483 pRunningFramesIn = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn);
76484 pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut);
76485
76486 if (channelsIn == channelsOut) {
76487 /* Fast path. Channel counts are the same. No need for an intermediary input buffer. */
76488 pWorkingBuffer = pRunningFramesOut;
76489 } else {
76490 /* Slow path. Channel counts are different. Need to use an intermediary input buffer. */
76491 pWorkingBuffer = temp;
76492 if (framesAvailableOut > tempCapInFrames) {
76493 framesAvailableOut = tempCapInFrames;
76494 }
76495 }
76496
76497 /* First is resampler. */
76498 if (isPitchingEnabled) {
76499 ma_uint64 resampleFrameCountIn = framesAvailableIn;
76500 ma_uint64 resampleFrameCountOut = framesAvailableOut;
76501
76502 ma_linear_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut);
76503 isWorkingBufferValid = MA_TRUE;
76504
76505 framesJustProcessedIn = (ma_uint32)resampleFrameCountIn;
76506 framesJustProcessedOut = (ma_uint32)resampleFrameCountOut;
76507 } else {
76508 framesJustProcessedIn = ma_min(framesAvailableIn, framesAvailableOut);
76509 framesJustProcessedOut = framesJustProcessedIn; /* When no resampling is being performed, the number of output frames is the same as input frames. */
76510 }
76511
76512 /* Fading. */
76513 if (isFadingEnabled) {
76514 if (isWorkingBufferValid) {
76515 ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); /* In-place processing. */
76516 } else {
76517 ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut);
76518 isWorkingBufferValid = MA_TRUE;
76519 }
76520 }
76521
76522 /*
76523 If we're using smoothing, we won't be applying volume via the spatializer, but instead from a ma_gainer. In this case
76524 we'll want to apply our volume now.
76525 */
76526 if (isVolumeSmoothingEnabled) {
76527 if (isWorkingBufferValid) {
76528 ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut);
76529 } else {
76530 ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut);
76531 isWorkingBufferValid = MA_TRUE;
76532 }
76533 }
76534
76535 /*
76536 If at this point we still haven't actually done anything with the working buffer we need
76537 to just read straight from the input buffer.
76538 */
76539 if (isWorkingBufferValid == MA_FALSE) {
76540 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. */
76541 }
76542
76543 /* Spatialization. */
76544 if (isSpatializationEnabled) {
76545 ma_uint32 iListener;
76546
76547 /*
76548 When determining the listener to use, we first check to see if the sound is pinned to a
76549 specific listener. If so, we use that. Otherwise we just use the closest listener.
76550 */
76551 if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) {
76552 iListener = pEngineNode->pinnedListenerIndex;
76553 } else {
76554 ma_vec3f spatializerPosition = ma_spatializer_get_position(&pEngineNode->spatializer);
76555 iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, spatializerPosition.x, spatializerPosition.y, spatializerPosition.z);
76556 }
76557
76558 ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut);
76559 } else {
76560 /* No spatialization, but we still need to do channel conversion and master volume. */
76561 float volume;
76562 ma_engine_node_get_volume(pEngineNode, &volume); /* Should never fail. */
76563
76564 if (channelsIn == channelsOut) {
76565 /* No channel conversion required. Just copy straight to the output buffer. */
76566 if (isVolumeSmoothingEnabled) {
76567 /* Volume has already been applied. Just copy straight to the output buffer. */
76568 ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, ma_format_f32, channelsOut);
76569 } else {
76570 /* Volume has not been applied yet. Copy and apply volume in the same pass. */
76571 ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume);
76572 }
76573 } else {
76574 /* Channel conversion required. TODO: Add support for channel maps here. */
76575 ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode);
76576
76577 /* If we're using smoothing, the volume will have already been applied. */
76578 if (!isVolumeSmoothingEnabled) {
76579 ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume);
76580 }
76581 }
76582 }
76583
76584 /* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */
76585
76586 /* Panning. */
76587 if (isPanningEnabled) {
76588 ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut); /* In-place processing. */
76589 }
76590
76591 /* We're done for this chunk. */
76592 totalFramesProcessedIn += framesJustProcessedIn;
76593 totalFramesProcessedOut += framesJustProcessedOut;
76594
76595 /* 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. */
76596 if (framesJustProcessedOut == 0) {
76597 break;
76598 }
76599 }
76600
76601 /* At this point we're done processing. */
76602 *pFrameCountIn = totalFramesProcessedIn;
76603 *pFrameCountOut = totalFramesProcessedOut;
76604}
76605
76606static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
76607{
76608 /* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */
76609 ma_result result = MA_SUCCESS;
76610 ma_sound* pSound = (ma_sound*)pNode;
76611 ma_uint32 frameCount = *pFrameCountOut;
76612 ma_uint32 totalFramesRead = 0;
76613 ma_format dataSourceFormat;
76614 ma_uint32 dataSourceChannels;
76615 ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
76616 ma_uint32 tempCapInFrames;
76617 ma_uint64 seekTarget;
76618
76619 /* This is a data source node which means no input buses. */
76620 (void)ppFramesIn;
76621 (void)pFrameCountIn;
76622
76623 /* If we're marked at the end we need to stop the sound and do nothing. */
76624 if (ma_sound_at_end(pSound)) {
76625 ma_sound_stop(pSound);
76626 *pFrameCountOut = 0;
76627 return;
76628 }
76629
76630 /* If we're seeking, do so now before reading. */
76631 seekTarget = ma_atomic_load_64(&pSound->seekTarget);
76632 if (seekTarget != MA_SEEK_TARGET_NONE) {
76633 ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget);
76634
76635 /* Any time-dependant effects need to have their times updated. */
76636 ma_node_set_time(pSound, seekTarget);
76637
76638 ma_atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE);
76639 }
76640
76641 /*
76642 We want to update the pitch once. For sounds, this can be either at the start or at the end. If
76643 we don't force this to only ever be updating once, we could end up in a situation where
76644 retrieving the required input frame count ends up being different to what we actually retrieve.
76645 What could happen is that the required input frame count is calculated, the pitch is update,
76646 and then this processing function is called resulting in a different number of input frames
76647 being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else
76648 you'll hit the aforementioned bug.
76649 */
76650 ma_engine_node_update_pitch_if_required(&pSound->engineNode);
76651
76652 /*
76653 For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ
76654 from the main engine.
76655 */
76656 result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0);
76657 if (result == MA_SUCCESS) {
76658 tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels);
76659
76660 /* Keep reading until we've read as much as was requested or we reach the end of the data source. */
76661 while (totalFramesRead < frameCount) {
76662 ma_uint32 framesRemaining = frameCount - totalFramesRead;
76663 ma_uint32 framesToRead;
76664 ma_uint64 framesJustRead;
76665 ma_uint32 frameCountIn;
76666 ma_uint32 frameCountOut;
76667 const float* pRunningFramesIn;
76668 float* pRunningFramesOut;
76669
76670 /*
76671 The first thing we need to do is read into the temporary buffer. We can calculate exactly
76672 how many input frames we'll need after resampling.
76673 */
76674 framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining);
76675 if (framesToRead > tempCapInFrames) {
76676 framesToRead = tempCapInFrames;
76677 }
76678
76679 result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead);
76680
76681 /* 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. */
76682 if (result == MA_AT_END) {
76683 ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */
76684 }
76685
76686 pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_node_get_output_channels(pNode, 0));
76687
76688 frameCountIn = (ma_uint32)framesJustRead;
76689 frameCountOut = framesRemaining;
76690
76691 /* Convert if necessary. */
76692 if (dataSourceFormat == ma_format_f32) {
76693 /* Fast path. No data conversion necessary. */
76694 pRunningFramesIn = (float*)temp;
76695 ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);
76696 } else {
76697 /* 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. */
76698 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. */
76699 ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none);
76700
76701 /* Now that we have our samples in f32 format we can process like normal. */
76702 pRunningFramesIn = tempf32;
76703 ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);
76704 }
76705
76706 /* We should have processed all of our input frames since we calculated the required number of input frames at the top. */
76707 MA_ASSERT(frameCountIn == framesJustRead);
76708 totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */
76709
76710 if (result != MA_SUCCESS || ma_sound_at_end(pSound)) {
76711 break; /* Might have reached the end. */
76712 }
76713 }
76714 }
76715
76716 *pFrameCountOut = totalFramesRead;
76717}
76718
76719static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
76720{
76721 /*
76722 Make sure the pitch is updated before trying to read anything. It's important that this is done
76723 only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that
76724 ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(),
76725 and if another thread modifies the pitch just after that call it can result in a glitch due to
76726 the input rate changing.
76727 */
76728 ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
76729
76730 /* For groups, the input data has already been read and we just need to apply the effect. */
76731 ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
76732}
76733
76734static ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount)
76735{
76736 ma_uint64 inputFrameCount;
76737
76738 MA_ASSERT(pInputFrameCount != NULL);
76739
76740 /* Our pitch will affect this calculation. We need to update it. */
76741 ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
76742
76743 inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount);
76744 if (inputFrameCount > 0xFFFFFFFF) {
76745 inputFrameCount = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */
76746 }
76747
76748 *pInputFrameCount = (ma_uint32)inputFrameCount;
76749
76750 return MA_SUCCESS;
76751}
76752
76753
76754static ma_node_vtable g_ma_engine_node_vtable__sound =
76755{
76756 ma_engine_node_process_pcm_frames__sound,
76757 NULL, /* onGetRequiredInputFrameCount */
76758 0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */
76759 1, /* Sounds have one output bus. */
76760 0 /* Default flags. */
76761};
76762
76763static ma_node_vtable g_ma_engine_node_vtable__group =
76764{
76765 ma_engine_node_process_pcm_frames__group,
76766 ma_engine_node_get_required_input_frame_count__group,
76767 1, /* Groups have one input bus. */
76768 1, /* Groups have one output bus. */
76769 MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */
76770};
76771
76772
76773
76774static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig)
76775{
76776 ma_node_config baseNodeConfig;
76777
76778 if (pConfig->type == ma_engine_node_type_sound) {
76779 /* Sound. */
76780 baseNodeConfig = ma_node_config_init();
76781 baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound;
76782 baseNodeConfig.initialState = ma_node_state_stopped; /* Sounds are stopped by default. */
76783 } else {
76784 /* Group. */
76785 baseNodeConfig = ma_node_config_init();
76786 baseNodeConfig.vtable = &g_ma_engine_node_vtable__group;
76787 baseNodeConfig.initialState = ma_node_state_started; /* Groups are started by default. */
76788 }
76789
76790 return baseNodeConfig;
76791}
76792
76793static ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig)
76794{
76795 return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]);
76796}
76797
76798typedef struct
76799{
76800 size_t sizeInBytes;
76801 size_t baseNodeOffset;
76802 size_t resamplerOffset;
76803 size_t spatializerOffset;
76804 size_t gainerOffset;
76805} ma_engine_node_heap_layout;
76806
76807static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout)
76808{
76809 ma_result result;
76810 size_t tempHeapSize;
76811 ma_node_config baseNodeConfig;
76812 ma_linear_resampler_config resamplerConfig;
76813 ma_spatializer_config spatializerConfig;
76814 ma_gainer_config gainerConfig;
76815 ma_uint32 channelsIn;
76816 ma_uint32 channelsOut;
76817 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. */
76818
76819 MA_ASSERT(pHeapLayout);
76820
76821 MA_ZERO_OBJECT(pHeapLayout);
76822
76823 if (pConfig == NULL) {
76824 return MA_INVALID_ARGS;
76825 }
76826
76827 if (pConfig->pEngine == NULL) {
76828 return MA_INVALID_ARGS; /* An engine must be specified. */
76829 }
76830
76831 pHeapLayout->sizeInBytes = 0;
76832
76833 channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine);
76834 channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine);
76835
76836
76837 /* Base node. */
76838 baseNodeConfig = ma_engine_node_base_node_config_init(pConfig);
76839 baseNodeConfig.pInputChannels = &channelsIn;
76840 baseNodeConfig.pOutputChannels = &channelsOut;
76841
76842 result = ma_node_get_heap_size(ma_engine_get_node_graph(pConfig->pEngine), &baseNodeConfig, &tempHeapSize);
76843 if (result != MA_SUCCESS) {
76844 return result; /* Failed to retrieve the size of the heap for the base node. */
76845 }
76846
76847 pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes;
76848 pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
76849
76850
76851 /* Resmapler. */
76852 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. */
76853 resamplerConfig.lpfOrder = 0;
76854
76855 result = ma_linear_resampler_get_heap_size(&resamplerConfig, &tempHeapSize);
76856 if (result != MA_SUCCESS) {
76857 return result; /* Failed to retrieve the size of the heap for the resampler. */
76858 }
76859
76860 pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes;
76861 pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
76862
76863
76864 /* Spatializer. */
76865 spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig);
76866
76867 if (spatializerConfig.channelsIn == 2) {
76868 spatializerConfig.pChannelMapIn = defaultStereoChannelMap;
76869 }
76870
76871 result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize);
76872 if (result != MA_SUCCESS) {
76873 return result; /* Failed to retrieve the size of the heap for the spatializer. */
76874 }
76875
76876 pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes;
76877 pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
76878
76879
76880 /* Gainer. Will not be used if we are not using smoothing. */
76881 if (pConfig->volumeSmoothTimeInPCMFrames > 0) {
76882 gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames);
76883
76884 result = ma_gainer_get_heap_size(&gainerConfig, &tempHeapSize);
76885 if (result != MA_SUCCESS) {
76886 return result;
76887 }
76888
76889 pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes;
76890 pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
76891 }
76892
76893
76894 return MA_SUCCESS;
76895}
76896
76897MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes)
76898{
76899 ma_result result;
76900 ma_engine_node_heap_layout heapLayout;
76901
76902 if (pHeapSizeInBytes == NULL) {
76903 return MA_INVALID_ARGS;
76904 }
76905
76906 *pHeapSizeInBytes = 0;
76907
76908 result = ma_engine_node_get_heap_layout(pConfig, &heapLayout);
76909 if (result != MA_SUCCESS) {
76910 return result;
76911 }
76912
76913 *pHeapSizeInBytes = heapLayout.sizeInBytes;
76914
76915 return MA_SUCCESS;
76916}
76917
76918MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode)
76919{
76920 ma_result result;
76921 ma_engine_node_heap_layout heapLayout;
76922 ma_node_config baseNodeConfig;
76923 ma_linear_resampler_config resamplerConfig;
76924 ma_fader_config faderConfig;
76925 ma_spatializer_config spatializerConfig;
76926 ma_panner_config pannerConfig;
76927 ma_gainer_config gainerConfig;
76928 ma_uint32 channelsIn;
76929 ma_uint32 channelsOut;
76930 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. */
76931
76932 if (pEngineNode == NULL) {
76933 return MA_INVALID_ARGS;
76934 }
76935
76936 MA_ZERO_OBJECT(pEngineNode);
76937
76938 result = ma_engine_node_get_heap_layout(pConfig, &heapLayout);
76939 if (result != MA_SUCCESS) {
76940 return result;
76941 }
76942
76944 return MA_INVALID_ARGS; /* Invalid listener. */
76945 }
76946
76947 pEngineNode->_pHeap = pHeap;
76948 MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
76949
76950 pEngineNode->pEngine = pConfig->pEngine;
76951 pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine);
76953 pEngineNode->monoExpansionMode = pConfig->monoExpansionMode;
76954 ma_atomic_float_set(&pEngineNode->volume, 1);
76955 pEngineNode->pitch = 1;
76956 pEngineNode->oldPitch = 1;
76957 pEngineNode->oldDopplerPitch = 1;
76958 pEngineNode->isPitchDisabled = pConfig->isPitchDisabled;
76959 pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled;
76960 pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex;
76961 ma_atomic_float_set(&pEngineNode->fadeSettings.volumeBeg, 1);
76962 ma_atomic_float_set(&pEngineNode->fadeSettings.volumeEnd, 1);
76963 ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, (~(ma_uint64)0));
76964 ma_atomic_uint64_set(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames, (~(ma_uint64)0)); /* <-- Indicates that the fade should start immediately. */
76965
76966 channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine);
76967 channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine);
76968
76969 /*
76970 If the sample rate of the sound is different to the engine, make sure pitching is enabled so that the resampler
76971 is activated. Not doing this will result in the sound not being resampled if MA_SOUND_FLAG_NO_PITCH is used.
76972 */
76973 if (pEngineNode->sampleRate != ma_engine_get_sample_rate(pEngineNode->pEngine)) {
76974 pEngineNode->isPitchDisabled = MA_FALSE;
76975 }
76976
76977
76978 /* Base node. */
76979 baseNodeConfig = ma_engine_node_base_node_config_init(pConfig);
76980 baseNodeConfig.pInputChannels = &channelsIn;
76981 baseNodeConfig.pOutputChannels = &channelsOut;
76982
76983 result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode);
76984 if (result != MA_SUCCESS) {
76985 goto error0;
76986 }
76987
76988
76989 /*
76990 We can now initialize the effects we need in order to implement the engine node. There's a
76991 defined order of operations here, mainly centered around when we convert our channels from the
76992 data source's native channel count to the engine's channel count. As a rule, we want to do as
76993 much computation as possible before spatialization because there's a chance that will increase
76994 the channel count, thereby increasing the amount of work needing to be done to process.
76995 */
76996
76997 /* We'll always do resampling first. */
76998 resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], pEngineNode->sampleRate, ma_engine_get_sample_rate(pEngineNode->pEngine));
76999 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. */
77000
77001 result = ma_linear_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pEngineNode->resampler);
77002 if (result != MA_SUCCESS) {
77003 goto error1;
77004 }
77005
77006
77007 /* After resampling will come the fader. */
77008 faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine));
77009
77010 result = ma_fader_init(&faderConfig, &pEngineNode->fader);
77011 if (result != MA_SUCCESS) {
77012 goto error2;
77013 }
77014
77015
77016 /*
77017 Spatialization comes next. We spatialize based on the node's output channel count. It's up the caller to
77018 ensure channels counts link up correctly in the node graph.
77019 */
77020 spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig);
77021 spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames;
77022
77023 if (spatializerConfig.channelsIn == 2) {
77024 spatializerConfig.pChannelMapIn = defaultStereoChannelMap;
77025 }
77026
77027 result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer);
77028 if (result != MA_SUCCESS) {
77029 goto error2;
77030 }
77031
77032
77033 /*
77034 After spatialization comes panning. We need to do this after spatialization because otherwise we wouldn't
77035 be able to pan mono sounds.
77036 */
77037 pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]);
77038
77039 result = ma_panner_init(&pannerConfig, &pEngineNode->panner);
77040 if (result != MA_SUCCESS) {
77041 goto error3;
77042 }
77043
77044
77045 /* 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. */
77046 if (pConfig->volumeSmoothTimeInPCMFrames > 0) {
77047 gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames);
77048
77049 result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pEngineNode->volumeGainer);
77050 if (result != MA_SUCCESS) {
77051 goto error3;
77052 }
77053 }
77054
77055
77056 return MA_SUCCESS;
77057
77058 /* No need for allocation callbacks here because we use a preallocated heap. */
77059error3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL);
77060error2: ma_linear_resampler_uninit(&pEngineNode->resampler, NULL);
77061error1: ma_node_uninit(&pEngineNode->baseNode, NULL);
77062error0: return result;
77063}
77064
77065MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode)
77066{
77067 ma_result result;
77068 size_t heapSizeInBytes;
77069 void* pHeap;
77070
77071 result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes);
77072 if (result != MA_SUCCESS) {
77073 return result;
77074 }
77075
77076 if (heapSizeInBytes > 0) {
77077 pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
77078 if (pHeap == NULL) {
77079 return MA_OUT_OF_MEMORY;
77080 }
77081 } else {
77082 pHeap = NULL;
77083 }
77084
77085 result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode);
77086 if (result != MA_SUCCESS) {
77087 ma_free(pHeap, pAllocationCallbacks);
77088 return result;
77089 }
77090
77091 pEngineNode->_ownsHeap = MA_TRUE;
77092 return MA_SUCCESS;
77093}
77094
77095MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks)
77096{
77097 /*
77098 The base node always needs to be uninitialized first to ensure it's detached from the graph completely before we
77099 destroy anything that might be in the middle of being used by the processing function.
77100 */
77101 ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks);
77102
77103 /* Now that the node has been uninitialized we can safely uninitialize the rest. */
77104 if (pEngineNode->volumeSmoothTimeInPCMFrames > 0) {
77105 ma_gainer_uninit(&pEngineNode->volumeGainer, pAllocationCallbacks);
77106 }
77107
77108 ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks);
77109 ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks);
77110
77111 /* Free the heap last. */
77112 if (pEngineNode->_ownsHeap) {
77113 ma_free(pEngineNode->_pHeap, pAllocationCallbacks);
77114 }
77115}
77116
77117
77118MA_API ma_sound_config ma_sound_config_init(void)
77119{
77121}
77122
77123MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine)
77124{
77125 ma_sound_config config;
77126
77127 MA_ZERO_OBJECT(&config);
77128
77129 if (pEngine != NULL) {
77130 config.monoExpansionMode = pEngine->monoExpansionMode;
77131 } else {
77133 }
77134
77135 config.rangeEndInPCMFrames = ~((ma_uint64)0);
77136 config.loopPointEndInPCMFrames = ~((ma_uint64)0);
77137
77138 return config;
77139}
77140
77142{
77144}
77145
77147{
77148 ma_sound_group_config config;
77149
77150 MA_ZERO_OBJECT(&config);
77151
77152 if (pEngine != NULL) {
77153 config.monoExpansionMode = pEngine->monoExpansionMode;
77154 } else {
77156 }
77157
77158 return config;
77159}
77160
77161
77162MA_API ma_engine_config ma_engine_config_init(void)
77163{
77164 ma_engine_config config;
77165
77166 MA_ZERO_OBJECT(&config);
77167 config.listenerCount = 1; /* Always want at least one listener. */
77169
77170 return config;
77171}
77172
77173
77174#if !defined(MA_NO_DEVICE_IO)
77175static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
77176{
77177 ma_engine* pEngine = (ma_engine*)pDevice->pUserData;
77178
77179 (void)pFramesIn;
77180
77181 /*
77182 Experiment: Try processing a resource manager job if we're on the Emscripten build.
77183
77184 This serves two purposes:
77185
77186 1) It ensures jobs are actually processed at some point since we cannot guarantee that the
77187 caller is doing the right thing and calling ma_resource_manager_process_next_job(); and
77188
77189 2) It's an attempt at working around an issue where processing jobs on the Emscripten main
77190 loop doesn't work as well as it should. When trying to load sounds without the `DECODE`
77191 flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time
77192 before the callback is processed. I think it's got something to do with the single-
77193 threaded nature of Web, but I'm not entirely sure.
77194 */
77195 #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN)
77196 {
77197 if (pEngine->pResourceManager != NULL) {
77200 }
77201 }
77202 }
77203 #endif
77204
77205 ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL);
77206}
77207
77208static ma_uint32 ma_device__get_processing_size_in_frames(ma_device* pDevice)
77209{
77210 /*
77211 The processing size is the period size. The device can have a fixed sized processing size, or
77212 it can be decided by the backend in which case it can be variable.
77213 */
77214 if (pDevice->playback.intermediaryBufferCap > 0) {
77215 /* Using a fixed sized processing callback. */
77216 return pDevice->playback.intermediaryBufferCap;
77217 } else {
77218 /* Not using a fixed sized processing callback. Need to estimate the processing size based on the backend. */
77219 return pDevice->playback.internalPeriodSizeInFrames;
77220 }
77221}
77222#endif
77223
77224MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine)
77225{
77226 ma_result result;
77227 ma_node_graph_config nodeGraphConfig;
77228 ma_engine_config engineConfig;
77229 ma_spatializer_listener_config listenerConfig;
77230 ma_uint32 iListener;
77231
77232 if (pEngine == NULL) {
77233 return MA_INVALID_ARGS;
77234 }
77235
77236 MA_ZERO_OBJECT(pEngine);
77237
77238 /* The config is allowed to be NULL in which case we use defaults for everything. */
77239 if (pConfig != NULL) {
77240 engineConfig = *pConfig;
77241 } else {
77242 engineConfig = ma_engine_config_init();
77243 }
77244
77245 pEngine->monoExpansionMode = engineConfig.monoExpansionMode;
77247 pEngine->onProcess = engineConfig.onProcess;
77248 pEngine->pProcessUserData = engineConfig.pProcessUserData;
77249 ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks);
77250
77251 #if !defined(MA_NO_RESOURCE_MANAGER)
77252 {
77253 pEngine->pResourceManager = engineConfig.pResourceManager;
77254 }
77255 #endif
77256
77257 #if !defined(MA_NO_DEVICE_IO)
77258 {
77259 pEngine->pDevice = engineConfig.pDevice;
77260
77261 /* If we don't have a device, we need one. */
77262 if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) {
77263 ma_device_config deviceConfig;
77264
77265 pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks);
77266 if (pEngine->pDevice == NULL) {
77267 return MA_OUT_OF_MEMORY;
77268 }
77269
77271 deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID;
77272 deviceConfig.playback.format = ma_format_f32;
77273 deviceConfig.playback.channels = engineConfig.channels;
77274 deviceConfig.sampleRate = engineConfig.sampleRate;
77275 deviceConfig.dataCallback = (engineConfig.dataCallback != NULL) ? engineConfig.dataCallback : ma_engine_data_callback_internal;
77276 deviceConfig.pUserData = pEngine;
77277 deviceConfig.notificationCallback = engineConfig.notificationCallback;
77278 deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames;
77279 deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds;
77280 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. */
77281 deviceConfig.noClip = MA_TRUE; /* The engine will do clipping itself. */
77282
77283 if (engineConfig.pContext == NULL) {
77284 ma_context_config contextConfig = ma_context_config_init();
77285 contextConfig.allocationCallbacks = pEngine->allocationCallbacks;
77286 contextConfig.pLog = engineConfig.pLog;
77287
77288 /* If the engine config does not specify a log, use the resource manager's if we have one. */
77289 #ifndef MA_NO_RESOURCE_MANAGER
77290 {
77291 if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) {
77292 contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager);
77293 }
77294 }
77295 #endif
77296
77297 result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice);
77298 } else {
77299 result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice);
77300 }
77301
77302 if (result != MA_SUCCESS) {
77303 ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
77304 pEngine->pDevice = NULL;
77305 return result;
77306 }
77307
77308 pEngine->ownsDevice = MA_TRUE;
77309 }
77310
77311 /* Update the channel count and sample rate of the engine config so we can reference it below. */
77312 if (pEngine->pDevice != NULL) {
77313 engineConfig.channels = pEngine->pDevice->playback.channels;
77314 engineConfig.sampleRate = pEngine->pDevice->sampleRate;
77315
77316 /*
77317 The processing size used by the engine is determined by engineConfig.periodSizeInFrames. We want
77318 to make this equal to what the device is using for it's period size. If we don't do that, it's
77319 possible that the node graph will split it's processing into multiple passes which can introduce
77320 glitching.
77321 */
77322 engineConfig.periodSizeInFrames = ma_device__get_processing_size_in_frames(pEngine->pDevice);
77323 }
77324 }
77325 #endif
77326
77327 if (engineConfig.channels == 0 || engineConfig.sampleRate == 0) {
77328 return MA_INVALID_ARGS;
77329 }
77330
77331 pEngine->sampleRate = engineConfig.sampleRate;
77332
77333 /* The engine always uses either the log that was passed into the config, or the context's log is available. */
77334 if (engineConfig.pLog != NULL) {
77335 pEngine->pLog = engineConfig.pLog;
77336 } else {
77337 #if !defined(MA_NO_DEVICE_IO)
77338 {
77339 pEngine->pLog = ma_device_get_log(pEngine->pDevice);
77340 }
77341 #else
77342 {
77343 pEngine->pLog = NULL;
77344 }
77345 #endif
77346 }
77347
77348
77349 /* The engine is a node graph. This needs to be initialized after we have the device so we can determine the channel count. */
77350 nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels);
77351 nodeGraphConfig.processingSizeInFrames = engineConfig.periodSizeInFrames;
77352 nodeGraphConfig.preMixStackSizeInBytes = engineConfig.preMixStackSizeInBytes;
77353
77354 result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph);
77355 if (result != MA_SUCCESS) {
77356 goto on_error_1;
77357 }
77358
77359
77360 /* We need at least one listener. */
77361 if (engineConfig.listenerCount == 0) {
77362 engineConfig.listenerCount = 1;
77363 }
77364
77365 if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) {
77366 result = MA_INVALID_ARGS; /* Too many listeners. */
77367 goto on_error_1;
77368 }
77369
77370 for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) {
77372
77373 /*
77374 If we're using a device, use the device's channel map for the listener. Otherwise just use
77375 miniaudio's default channel map.
77376 */
77377 #if !defined(MA_NO_DEVICE_IO)
77378 {
77379 if (pEngine->pDevice != NULL) {
77380 /*
77381 Temporarily disabled. There is a subtle bug here where front-left and front-right
77382 will be used by the device's channel map, but this is not what we want to use for
77383 spatialization. Instead we want to use side-left and side-right. I need to figure
77384 out a better solution for this. For now, disabling the use of device channel maps.
77385 */
77386 /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/
77387 }
77388 }
77389 #endif
77390
77391 result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]); /* TODO: Change this to a pre-allocated heap. */
77392 if (result != MA_SUCCESS) {
77393 goto on_error_2;
77394 }
77395
77396 pEngine->listenerCount += 1;
77397 }
77398
77399
77400 /* Gain smoothing for spatialized sounds. */
77401 pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames;
77402 if (pEngine->gainSmoothTimeInFrames == 0) {
77403 ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds;
77404 if (gainSmoothTimeInMilliseconds == 0) {
77405 gainSmoothTimeInMilliseconds = 8;
77406 }
77407
77408 pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000; /* 8ms by default. */
77409 }
77410
77411
77412 /* We need a resource manager. */
77413 #ifndef MA_NO_RESOURCE_MANAGER
77414 {
77415 if (pEngine->pResourceManager == NULL) {
77416 ma_resource_manager_config resourceManagerConfig;
77417
77418 pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks);
77419 if (pEngine->pResourceManager == NULL) {
77420 result = MA_OUT_OF_MEMORY;
77421 goto on_error_2;
77422 }
77423
77424 resourceManagerConfig = ma_resource_manager_config_init();
77425 resourceManagerConfig.pLog = pEngine->pLog; /* Always use the engine's log for internally-managed resource managers. */
77426 resourceManagerConfig.decodedFormat = ma_format_f32;
77427 resourceManagerConfig.decodedChannels = 0; /* Leave the decoded channel count as 0 so we can get good spatialization. */
77428 resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine);
77429 ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks);
77430 resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS;
77431
77432 /* The Emscripten build cannot use threads unless it's targeting pthreads. */
77433 #if defined(MA_EMSCRIPTEN) && !defined(__EMSCRIPTEN_PTHREADS__)
77434 {
77435 resourceManagerConfig.jobThreadCount = 0;
77436 resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
77437 }
77438 #endif
77439
77440 result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager);
77441 if (result != MA_SUCCESS) {
77442 goto on_error_3;
77443 }
77444
77445 pEngine->ownsResourceManager = MA_TRUE;
77446 }
77447 }
77448 #endif
77449
77450 /* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */
77451 pEngine->inlinedSoundLock = 0;
77452 pEngine->pInlinedSoundHead = NULL;
77453
77454 /* Start the engine if required. This should always be the last step. */
77455 #if !defined(MA_NO_DEVICE_IO)
77456 {
77457 if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) {
77458 result = ma_engine_start(pEngine);
77459 if (result != MA_SUCCESS) {
77460 goto on_error_4; /* Failed to start the engine. */
77461 }
77462 }
77463 }
77464 #endif
77465
77466 return MA_SUCCESS;
77467
77468#if !defined(MA_NO_DEVICE_IO)
77469on_error_4:
77470#endif
77471#if !defined(MA_NO_RESOURCE_MANAGER)
77472on_error_3:
77473 if (pEngine->ownsResourceManager) {
77474 ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks);
77475 }
77476#endif /* MA_NO_RESOURCE_MANAGER */
77477on_error_2:
77478 for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
77479 ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks);
77480 }
77481
77483on_error_1:
77484 #if !defined(MA_NO_DEVICE_IO)
77485 {
77486 if (pEngine->ownsDevice) {
77487 ma_device_uninit(pEngine->pDevice);
77488 ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
77489 }
77490 }
77491 #endif
77492
77493 return result;
77494}
77495
77496MA_API void ma_engine_uninit(ma_engine* pEngine)
77497{
77498 ma_uint32 iListener;
77499
77500 if (pEngine == NULL) {
77501 return;
77502 }
77503
77504 /* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */
77505 #if !defined(MA_NO_DEVICE_IO)
77506 {
77507 if (pEngine->ownsDevice) {
77508 ma_device_uninit(pEngine->pDevice);
77509 ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
77510 } else {
77511 if (pEngine->pDevice != NULL) {
77512 ma_device_stop(pEngine->pDevice);
77513 }
77514 }
77515 }
77516 #endif
77517
77518 /*
77519 All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case
77520 I want to do some kind of garbage collection later on.
77521 */
77523 {
77524 for (;;) {
77525 ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead;
77526 if (pSoundToDelete == NULL) {
77527 break; /* Done. */
77528 }
77529
77530 pEngine->pInlinedSoundHead = pSoundToDelete->pNext;
77531
77532 ma_sound_uninit(&pSoundToDelete->sound);
77533 ma_free(pSoundToDelete, &pEngine->allocationCallbacks);
77534 }
77535 }
77537
77538 for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
77539 ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks);
77540 }
77541
77542 /* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */
77544
77545 /* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */
77546#ifndef MA_NO_RESOURCE_MANAGER
77547 if (pEngine->ownsResourceManager) {
77549 ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks);
77550 }
77551#endif
77552}
77553
77554MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
77555{
77556 ma_result result;
77557 ma_uint64 framesRead = 0;
77558
77559 if (pFramesRead != NULL) {
77560 *pFramesRead = 0;
77561 }
77562
77563 result = ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, &framesRead);
77564 if (result != MA_SUCCESS) {
77565 return result;
77566 }
77567
77568 if (pFramesRead != NULL) {
77569 *pFramesRead = framesRead;
77570 }
77571
77572 if (pEngine->onProcess) {
77573 pEngine->onProcess(pEngine->pProcessUserData, (float*)pFramesOut, framesRead); /* Safe cast to float* because the engine always works on floating point samples. */
77574 }
77575
77576 return MA_SUCCESS;
77577}
77578
77580{
77581 if (pEngine == NULL) {
77582 return NULL;
77583 }
77584
77585 return &pEngine->nodeGraph;
77586}
77587
77588#if !defined(MA_NO_RESOURCE_MANAGER)
77590{
77591 if (pEngine == NULL) {
77592 return NULL;
77593 }
77594
77595 #if !defined(MA_NO_RESOURCE_MANAGER)
77596 {
77597 return pEngine->pResourceManager;
77598 }
77599 #else
77600 {
77601 return NULL;
77602 }
77603 #endif
77604}
77605#endif
77606
77608{
77609 if (pEngine == NULL) {
77610 return NULL;
77611 }
77612
77613 #if !defined(MA_NO_DEVICE_IO)
77614 {
77615 return pEngine->pDevice;
77616 }
77617 #else
77618 {
77619 return NULL;
77620 }
77621 #endif
77622}
77623
77624MA_API ma_log* ma_engine_get_log(ma_engine* pEngine)
77625{
77626 if (pEngine == NULL) {
77627 return NULL;
77628 }
77629
77630 if (pEngine->pLog != NULL) {
77631 return pEngine->pLog;
77632 } else {
77633 #if !defined(MA_NO_DEVICE_IO)
77634 {
77635 return ma_device_get_log(ma_engine_get_device(pEngine));
77636 }
77637 #else
77638 {
77639 return NULL;
77640 }
77641 #endif
77642 }
77643}
77644
77646{
77647 return ma_node_graph_get_endpoint(&pEngine->nodeGraph);
77648}
77649
77651{
77652 return ma_node_graph_get_time(&pEngine->nodeGraph);
77653}
77654
77656{
77657 return ma_engine_get_time_in_pcm_frames(pEngine) * 1000 / ma_engine_get_sample_rate(pEngine);
77658}
77659
77661{
77662 return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime);
77663}
77664
77666{
77667 return ma_engine_set_time_in_pcm_frames(pEngine, globalTime * ma_engine_get_sample_rate(pEngine) / 1000);
77668}
77669
77671{
77672 return ma_engine_get_time_in_pcm_frames(pEngine);
77673}
77674
77676{
77677 return ma_engine_set_time_in_pcm_frames(pEngine, globalTime);
77678}
77679
77681{
77682 return ma_node_graph_get_channels(&pEngine->nodeGraph);
77683}
77684
77686{
77687 if (pEngine == NULL) {
77688 return 0;
77689 }
77690
77691 return pEngine->sampleRate;
77692}
77693
77694
77696{
77697 ma_result result;
77698
77699 if (pEngine == NULL) {
77700 return MA_INVALID_ARGS;
77701 }
77702
77703 #if !defined(MA_NO_DEVICE_IO)
77704 {
77705 if (pEngine->pDevice != NULL) {
77706 result = ma_device_start(pEngine->pDevice);
77707 } else {
77708 result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "starting" the engine. */
77709 }
77710 }
77711 #else
77712 {
77713 result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "starting" the engine. */
77714 }
77715 #endif
77716
77717 if (result != MA_SUCCESS) {
77718 return result;
77719 }
77720
77721 return MA_SUCCESS;
77722}
77723
77725{
77726 ma_result result;
77727
77728 if (pEngine == NULL) {
77729 return MA_INVALID_ARGS;
77730 }
77731
77732 #if !defined(MA_NO_DEVICE_IO)
77733 {
77734 if (pEngine->pDevice != NULL) {
77735 result = ma_device_stop(pEngine->pDevice);
77736 } else {
77737 result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "stopping" the engine. */
77738 }
77739 }
77740 #else
77741 {
77742 result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "stopping" the engine. */
77743 }
77744 #endif
77745
77746 if (result != MA_SUCCESS) {
77747 return result;
77748 }
77749
77750 return MA_SUCCESS;
77751}
77752
77753MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume)
77754{
77755 if (pEngine == NULL) {
77756 return MA_INVALID_ARGS;
77757 }
77758
77760}
77761
77763{
77764 if (pEngine == NULL) {
77765 return 0;
77766 }
77767
77769}
77770
77771MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB)
77772{
77773 return ma_engine_set_volume(pEngine, ma_volume_db_to_linear(gainDB));
77774}
77775
77777{
77779}
77780
77781
77783{
77784 if (pEngine == NULL) {
77785 return 0;
77786 }
77787
77788 return pEngine->listenerCount;
77789}
77790
77791MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ)
77792{
77793 ma_uint32 iListener;
77794 ma_uint32 iListenerClosest;
77795 float closestLen2 = MA_FLT_MAX;
77796
77797 if (pEngine == NULL || pEngine->listenerCount == 1) {
77798 return 0;
77799 }
77800
77801 iListenerClosest = 0;
77802 for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
77803 if (ma_engine_listener_is_enabled(pEngine, iListener)) {
77804 float len2 = ma_vec3f_len2(ma_vec3f_sub(ma_spatializer_listener_get_position(&pEngine->listeners[iListener]), ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ)));
77805 if (closestLen2 > len2) {
77806 closestLen2 = len2;
77807 iListenerClosest = iListener;
77808 }
77809 }
77810 }
77811
77812 MA_ASSERT(iListenerClosest < 255);
77813 return iListenerClosest;
77814}
77815
77816MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
77817{
77818 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
77819 return;
77820 }
77821
77822 ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z);
77823}
77824
77825MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex)
77826{
77827 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
77828 return ma_vec3f_init_3f(0, 0, 0);
77829 }
77830
77831 return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]);
77832}
77833
77834MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
77835{
77836 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
77837 return;
77838 }
77839
77840 ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z);
77841}
77842
77843MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex)
77844{
77845 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
77846 return ma_vec3f_init_3f(0, 0, -1);
77847 }
77848
77849 return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]);
77850}
77851
77852MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
77853{
77854 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
77855 return;
77856 }
77857
77858 ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z);
77859}
77860
77861MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex)
77862{
77863 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
77864 return ma_vec3f_init_3f(0, 0, 0);
77865 }
77866
77867 return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]);
77868}
77869
77870MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
77871{
77872 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
77873 return;
77874 }
77875
77876 ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain);
77877}
77878
77879MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
77880{
77881 if (pInnerAngleInRadians != NULL) {
77882 *pInnerAngleInRadians = 0;
77883 }
77884
77885 if (pOuterAngleInRadians != NULL) {
77886 *pOuterAngleInRadians = 0;
77887 }
77888
77889 if (pOuterGain != NULL) {
77890 *pOuterGain = 0;
77891 }
77892
77893 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
77894 return;
77895 }
77896
77897 ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
77898}
77899
77900MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
77901{
77902 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
77903 return;
77904 }
77905
77906 ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z);
77907}
77908
77909MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex)
77910{
77911 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
77912 return ma_vec3f_init_3f(0, 1, 0);
77913 }
77914
77915 return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]);
77916}
77917
77918MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled)
77919{
77920 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
77921 return;
77922 }
77923
77924 ma_spatializer_listener_set_enabled(&pEngine->listeners[listenerIndex], isEnabled);
77925}
77926
77928{
77929 if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
77930 return MA_FALSE;
77931 }
77932
77933 return ma_spatializer_listener_is_enabled(&pEngine->listeners[listenerIndex]);
77934}
77935
77936
77937#ifndef MA_NO_RESOURCE_MANAGER
77938MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex)
77939{
77940 ma_result result = MA_SUCCESS;
77941 ma_sound_inlined* pSound = NULL;
77942 ma_sound_inlined* pNextSound = NULL;
77943
77944 if (pEngine == NULL || pFilePath == NULL) {
77945 return MA_INVALID_ARGS;
77946 }
77947
77948 /* Attach to the endpoint node if nothing is specified. */
77949 if (pNode == NULL) {
77950 pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph);
77951 nodeInputBusIndex = 0;
77952 }
77953
77954 /*
77955 We want to check if we can recycle an already-allocated inlined sound. Since this is just a
77956 helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep
77957 the implementation simple. Maybe this can be optimized later if there's enough demand, but
77958 if this function is being used it probably means the caller doesn't really care too much.
77959
77960 What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise
77961 we just keep iterating. If we reach the end without finding a sound to recycle we just
77962 allocate a new one. This doesn't scale well for a massive number of sounds being played
77963 simultaneously as we don't ever actually free the sound objects. Some kind of garbage
77964 collection routine might be valuable for this which I'll think about.
77965 */
77967 {
77968 ma_uint32 soundFlags = 0;
77969
77970 for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) {
77971 if (ma_sound_at_end(&pNextSound->sound)) {
77972 /*
77973 The sound is at the end which means it's available for recycling. All we need to do
77974 is uninitialize it and reinitialize it. All we're doing is recycling memory.
77975 */
77976 pSound = pNextSound;
77977 ma_atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1);
77978 break;
77979 }
77980 }
77981
77982 if (pSound != NULL) {
77983 /*
77984 We actually want to detach the sound from the list here. The reason is because we want the sound
77985 to be in a consistent state at the non-recycled case to simplify the logic below.
77986 */
77987 if (pEngine->pInlinedSoundHead == pSound) {
77988 pEngine->pInlinedSoundHead = pSound->pNext;
77989 }
77990
77991 if (pSound->pPrev != NULL) {
77992 pSound->pPrev->pNext = pSound->pNext;
77993 }
77994 if (pSound->pNext != NULL) {
77995 pSound->pNext->pPrev = pSound->pPrev;
77996 }
77997
77998 /* Now the previous sound needs to be uninitialized. */
77999 ma_sound_uninit(&pNextSound->sound);
78000 } else {
78001 /* No sound available for recycling. Allocate one now. */
78002 pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks);
78003 }
78004
78005 if (pSound != NULL) { /* Safety check for the allocation above. */
78006 /*
78007 At this point we should have memory allocated for the inlined sound. We just need
78008 to initialize it like a normal sound now.
78009 */
78010 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. */
78011 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. */
78012 soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */
78013 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. */
78014
78015 result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound);
78016 if (result == MA_SUCCESS) {
78017 /* Now attach the sound to the graph. */
78018 result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex);
78019 if (result == MA_SUCCESS) {
78020 /* 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. */
78021 pSound->pNext = pEngine->pInlinedSoundHead;
78022 pSound->pPrev = NULL;
78023
78024 pEngine->pInlinedSoundHead = pSound; /* <-- This is what attaches the sound to the list. */
78025 if (pSound->pNext != NULL) {
78026 pSound->pNext->pPrev = pSound;
78027 }
78028 } else {
78029 ma_free(pSound, &pEngine->allocationCallbacks);
78030 }
78031 } else {
78032 ma_free(pSound, &pEngine->allocationCallbacks);
78033 }
78034 } else {
78035 result = MA_OUT_OF_MEMORY;
78036 }
78037 }
78039
78040 if (result != MA_SUCCESS) {
78041 return result;
78042 }
78043
78044 /* Finally we can start playing the sound. */
78045 result = ma_sound_start(&pSound->sound);
78046 if (result != MA_SUCCESS) {
78047 /* Failed to start the sound. We need to mark it for recycling and return an error. */
78048 ma_atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE);
78049 return result;
78050 }
78051
78052 ma_atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1);
78053 return result;
78054}
78055
78056MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup)
78057{
78058 return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0);
78059}
78060#endif
78061
78062
78063static ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound)
78064{
78065 if (pSound == NULL) {
78066 return MA_INVALID_ARGS;
78067 }
78068
78069 MA_ZERO_OBJECT(pSound);
78070 pSound->seekTarget = MA_SEEK_TARGET_NONE;
78071
78072 if (pEngine == NULL) {
78073 return MA_INVALID_ARGS;
78074 }
78075
78076 return MA_SUCCESS;
78077}
78078
78079static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
78080{
78081 ma_result result;
78082 ma_engine_node_config engineNodeConfig;
78083 ma_engine_node_type type; /* Will be set to ma_engine_node_type_group if no data source is specified. */
78084
78085 /* Do not clear pSound to zero here - that's done at a higher level with ma_sound_preinit(). */
78086 MA_ASSERT(pEngine != NULL);
78087 MA_ASSERT(pSound != NULL);
78088
78089 if (pConfig == NULL) {
78090 return MA_INVALID_ARGS;
78091 }
78092
78093 pSound->pDataSource = pConfig->pDataSource;
78094
78095 if (pConfig->pDataSource != NULL) {
78097 } else {
78099 }
78100
78101 /*
78102 Sounds are engine nodes. Before we can initialize this we need to determine the channel count.
78103 If we can't do this we need to abort. It's up to the caller to ensure they're using a data
78104 source that provides this information upfront.
78105 */
78106 engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags);
78107 engineNodeConfig.channelsIn = pConfig->channelsIn;
78108 engineNodeConfig.channelsOut = pConfig->channelsOut;
78109 engineNodeConfig.volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames;
78110 engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode;
78111
78112 if (engineNodeConfig.volumeSmoothTimeInPCMFrames == 0) {
78114 }
78115
78116 /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */
78117 if (pConfig->pDataSource != NULL) {
78118 result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate, NULL, 0);
78119 if (result != MA_SUCCESS) {
78120 return result; /* Failed to retrieve the channel count. */
78121 }
78122
78123 if (engineNodeConfig.channelsIn == 0) {
78124 return MA_INVALID_OPERATION; /* Invalid channel count. */
78125 }
78126
78127 if (engineNodeConfig.channelsOut == MA_SOUND_SOURCE_CHANNEL_COUNT) {
78128 engineNodeConfig.channelsOut = engineNodeConfig.channelsIn;
78129 }
78130 }
78131
78132
78133 /* Getting here means we should have a valid channel count and we can initialize the engine node. */
78134 result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode);
78135 if (result != MA_SUCCESS) {
78136 return result;
78137 }
78138
78139 /* If no attachment is specified, attach the sound straight to the endpoint. */
78140 if (pConfig->pInitialAttachment == NULL) {
78141 /* No group. Attach straight to the endpoint by default, unless the caller has requested that it not. */
78142 if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) {
78143 result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0);
78144 }
78145 } else {
78146 /* 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. */
78147 result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex);
78148 }
78149
78150 if (result != MA_SUCCESS) {
78152 return result;
78153 }
78154
78155
78156 /* Apply initial range and looping state to the data source if applicable. */
78157 if (pConfig->rangeBegInPCMFrames != 0 || pConfig->rangeEndInPCMFrames != ~((ma_uint64)0)) {
78159 }
78160
78161 if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) {
78163 }
78164
78165 ma_sound_set_looping(pSound, pConfig->isLooping || ((pConfig->flags & MA_SOUND_FLAG_LOOPING) != 0));
78166
78167 return MA_SUCCESS;
78168}
78169
78170#ifndef MA_NO_RESOURCE_MANAGER
78171MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
78172{
78173 ma_result result = MA_SUCCESS;
78174 ma_uint32 flags;
78175 ma_sound_config config;
78176 ma_resource_manager_pipeline_notifications notifications;
78177
78178 /*
78179 The engine requires knowledge of the channel count of the underlying data source before it can
78180 initialize the sound. Therefore, we need to make the resource manager wait until initialization
78181 of the underlying data source to be initialized so we can get access to the channel count. To
78182 do this, the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT is forced.
78183
78184 Because we're initializing the data source before the sound, there's a chance the notification
78185 will get triggered before this function returns. This is OK, so long as the caller is aware of
78186 it and can avoid accessing the sound from within the notification.
78187 */
78189 if (pConfig->isLooping) {
78191 }
78192
78194 if (pSound->pResourceManagerDataSource == NULL) {
78195 return MA_OUT_OF_MEMORY;
78196 }
78197
78198 /* Removed in 0.12. Set pDoneFence on the notifications. */
78199 notifications = pConfig->initNotifications;
78200 if (pConfig->pDoneFence != NULL && notifications.done.pFence == NULL) {
78201 notifications.done.pFence = pConfig->pDoneFence;
78202 }
78203
78204 /*
78205 We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does
78206 not return prematurely before the sound has finished initializing.
78207 */
78208 if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); }
78209 {
78210 ma_resource_manager_data_source_config resourceManagerDataSourceConfig = ma_resource_manager_data_source_config_init();
78211 resourceManagerDataSourceConfig.pFilePath = pConfig->pFilePath;
78212 resourceManagerDataSourceConfig.pFilePathW = pConfig->pFilePathW;
78213 resourceManagerDataSourceConfig.flags = flags;
78214 resourceManagerDataSourceConfig.pNotifications = &notifications;
78215 resourceManagerDataSourceConfig.initialSeekPointInPCMFrames = pConfig->initialSeekPointInPCMFrames;
78216 resourceManagerDataSourceConfig.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames;
78217 resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames;
78218 resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames;
78219 resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames;
78220 resourceManagerDataSourceConfig.isLooping = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0;
78221
78222 result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource);
78223 if (result != MA_SUCCESS) {
78225 goto done;
78226 }
78227
78228 pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */
78229
78230 /* We need to use a slightly customized version of the config so we'll need to make a copy. */
78231 config = *pConfig;
78232 config.pFilePath = NULL;
78233 config.pFilePathW = NULL;
78234 config.pDataSource = pSound->pResourceManagerDataSource;
78235
78236 result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound);
78237 if (result != MA_SUCCESS) {
78240 MA_ZERO_OBJECT(pSound);
78241 goto done;
78242 }
78243 }
78244done:
78245 if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); }
78246 return result;
78247}
78248
78249MA_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)
78250{
78251 ma_sound_config config;
78252
78253 if (pFilePath == NULL) {
78254 return MA_INVALID_ARGS;
78255 }
78256
78257 config = ma_sound_config_init_2(pEngine);
78258 config.pFilePath = pFilePath;
78259 config.flags = flags;
78260 config.pInitialAttachment = pGroup;
78261 config.pDoneFence = pDoneFence;
78262
78263 return ma_sound_init_ex(pEngine, &config, pSound);
78264}
78265
78266MA_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)
78267{
78268 ma_sound_config config;
78269
78270 if (pFilePath == NULL) {
78271 return MA_INVALID_ARGS;
78272 }
78273
78274 config = ma_sound_config_init_2(pEngine);
78275 config.pFilePathW = pFilePath;
78276 config.flags = flags;
78277 config.pInitialAttachment = pGroup;
78278 config.pDoneFence = pDoneFence;
78279
78280 return ma_sound_init_ex(pEngine, &config, pSound);
78281}
78282
78283MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound)
78284{
78285 ma_result result;
78286 ma_sound_config config;
78287
78288 result = ma_sound_preinit(pEngine, pSound);
78289 if (result != MA_SUCCESS) {
78290 return result;
78291 }
78292
78293 if (pExistingSound == NULL) {
78294 return MA_INVALID_ARGS;
78295 }
78296
78297 /* Cloning only works for data buffers (not streams) that are loaded from the resource manager. */
78298 if (pExistingSound->pResourceManagerDataSource == NULL) {
78299 return MA_INVALID_OPERATION;
78300 }
78301
78302 /*
78303 We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream)
78304 this will fail.
78305 */
78307 if (pSound->pResourceManagerDataSource == NULL) {
78308 return MA_OUT_OF_MEMORY;
78309 }
78310
78312 if (result != MA_SUCCESS) {
78314 return result;
78315 }
78316
78317 config = ma_sound_config_init_2(pEngine);
78318 config.pDataSource = pSound->pResourceManagerDataSource;
78319 config.flags = flags;
78320 config.pInitialAttachment = pGroup;
78321 config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode;
78323
78324 result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound);
78325 if (result != MA_SUCCESS) {
78328 MA_ZERO_OBJECT(pSound);
78329 return result;
78330 }
78331
78332 /* Make sure the sound is marked as the owner of the data source or else it will never get uninitialized. */
78333 pSound->ownsDataSource = MA_TRUE;
78334
78335 return MA_SUCCESS;
78336}
78337#endif
78338
78340{
78341 ma_sound_config config = ma_sound_config_init_2(pEngine);
78342 config.pDataSource = pDataSource;
78343 config.flags = flags;
78344 config.pInitialAttachment = pGroup;
78345 return ma_sound_init_ex(pEngine, &config, pSound);
78346}
78347
78348MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
78349{
78350 ma_result result;
78351
78352 result = ma_sound_preinit(pEngine, pSound);
78353 if (result != MA_SUCCESS) {
78354 return result;
78355 }
78356
78357 if (pConfig == NULL) {
78358 return MA_INVALID_ARGS;
78359 }
78360
78361 pSound->endCallback = pConfig->endCallback;
78362 pSound->pEndCallbackUserData = pConfig->pEndCallbackUserData;
78363
78364 /* We need to load the sound differently depending on whether or not we're loading from a file. */
78365#ifndef MA_NO_RESOURCE_MANAGER
78366 if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) {
78367 return ma_sound_init_from_file_internal(pEngine, pConfig, pSound);
78368 } else
78369#endif
78370 {
78371 /*
78372 Getting here means we're not loading from a file. We may be loading from an already-initialized
78373 data source, or none at all. If we aren't specifying any data source, we'll be initializing
78374 the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this
78375 for us, so no special treatment required here.
78376 */
78377 return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound);
78378 }
78379}
78380
78381MA_API void ma_sound_uninit(ma_sound* pSound)
78382{
78383 if (pSound == NULL) {
78384 return;
78385 }
78386
78387 /*
78388 Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done
78389 so which makes thread safety beyond this point trivial.
78390 */
78392
78393 /* 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. */
78394#ifndef MA_NO_RESOURCE_MANAGER
78395 if (pSound->ownsDataSource) {
78398 pSound->pDataSource = NULL;
78399 }
78400#else
78401 MA_ASSERT(pSound->ownsDataSource == MA_FALSE);
78402#endif
78403}
78404
78406{
78407 if (pSound == NULL) {
78408 return NULL;
78409 }
78410
78411 return pSound->engineNode.pEngine;
78412}
78413
78415{
78416 if (pSound == NULL) {
78417 return NULL;
78418 }
78419
78420 return pSound->pDataSource;
78421}
78422
78424{
78425 if (pSound == NULL) {
78426 return MA_INVALID_ARGS;
78427 }
78428
78429 /* If the sound is already playing, do nothing. */
78430 if (ma_sound_is_playing(pSound)) {
78431 return MA_SUCCESS;
78432 }
78433
78434 /* If the sound is at the end it means we want to start from the start again. */
78435 if (ma_sound_at_end(pSound)) {
78437 if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) {
78438 return result; /* Failed to seek back to the start. */
78439 }
78440
78441 /* Make sure we clear the end indicator. */
78442 ma_atomic_exchange_32(&pSound->atEnd, MA_FALSE);
78443 }
78444
78445 /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */
78447
78448 return MA_SUCCESS;
78449}
78450
78452{
78453 if (pSound == NULL) {
78454 return MA_INVALID_ARGS;
78455 }
78456
78457 /* This will stop the sound immediately. Use ma_sound_set_stop_time() to stop the sound at a specific time. */
78459
78460 return MA_SUCCESS;
78461}
78462
78464{
78465 if (pSound == NULL) {
78466 return MA_INVALID_ARGS;
78467 }
78468
78469 /* Stopping with a fade out requires us to schedule the stop into the future by the fade length. */
78470 ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames);
78471
78472 return MA_SUCCESS;
78473}
78474
78476{
78477 ma_uint64 sampleRate;
78478
78479 if (pSound == NULL) {
78480 return MA_INVALID_ARGS;
78481 }
78482
78483 sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound));
78484
78485 return ma_sound_stop_with_fade_in_pcm_frames(pSound, (fadeLengthInMilliseconds * sampleRate) / 1000);
78486}
78487
78488MA_API void ma_sound_set_volume(ma_sound* pSound, float volume)
78489{
78490 if (pSound == NULL) {
78491 return;
78492 }
78493
78494 ma_engine_node_set_volume(&pSound->engineNode, volume);
78495}
78496
78497MA_API float ma_sound_get_volume(const ma_sound* pSound)
78498{
78499 float volume = 0;
78500
78501 if (pSound == NULL) {
78502 return 0;
78503 }
78504
78505 ma_engine_node_get_volume(&pSound->engineNode, &volume);
78506
78507 return volume;
78508}
78509
78510MA_API void ma_sound_set_pan(ma_sound* pSound, float pan)
78511{
78512 if (pSound == NULL) {
78513 return;
78514 }
78515
78516 ma_panner_set_pan(&pSound->engineNode.panner, pan);
78517}
78518
78519MA_API float ma_sound_get_pan(const ma_sound* pSound)
78520{
78521 if (pSound == NULL) {
78522 return 0;
78523 }
78524
78525 return ma_panner_get_pan(&pSound->engineNode.panner);
78526}
78527
78528MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode)
78529{
78530 if (pSound == NULL) {
78531 return;
78532 }
78533
78534 ma_panner_set_mode(&pSound->engineNode.panner, panMode);
78535}
78536
78538{
78539 if (pSound == NULL) {
78540 return ma_pan_mode_balance;
78541 }
78542
78543 return ma_panner_get_mode(&pSound->engineNode.panner);
78544}
78545
78546MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch)
78547{
78548 if (pSound == NULL) {
78549 return;
78550 }
78551
78552 if (pitch <= 0) {
78553 return;
78554 }
78555
78556 ma_atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, ma_atomic_memory_order_release);
78557}
78558
78559MA_API float ma_sound_get_pitch(const ma_sound* pSound)
78560{
78561 if (pSound == NULL) {
78562 return 0;
78563 }
78564
78565 return ma_atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */
78566}
78567
78569{
78570 if (pSound == NULL) {
78571 return;
78572 }
78573
78574 ma_atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, ma_atomic_memory_order_release);
78575}
78576
78578{
78579 if (pSound == NULL) {
78580 return MA_FALSE;
78581 }
78582
78583 return ma_engine_node_is_spatialization_enabled(&pSound->engineNode);
78584}
78585
78587{
78588 if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) {
78589 return;
78590 }
78591
78592 ma_atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, ma_atomic_memory_order_release);
78593}
78594
78596{
78597 if (pSound == NULL) {
78599 }
78600
78601 return ma_atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, ma_atomic_memory_order_acquire);
78602}
78603
78605{
78606 ma_uint32 listenerIndex;
78607
78608 if (pSound == NULL) {
78609 return 0;
78610 }
78611
78612 listenerIndex = ma_sound_get_pinned_listener_index(pSound);
78613 if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) {
78614 ma_vec3f position = ma_sound_get_position(pSound);
78615 return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z);
78616 }
78617
78618 return listenerIndex;
78619}
78620
78622{
78623 ma_vec3f relativePos;
78624 ma_engine* pEngine;
78625
78626 if (pSound == NULL) {
78627 return ma_vec3f_init_3f(0, 0, -1);
78628 }
78629
78630 pEngine = ma_sound_get_engine(pSound);
78631 if (pEngine == NULL) {
78632 return ma_vec3f_init_3f(0, 0, -1);
78633 }
78634
78636
78637 return ma_vec3f_normalize(ma_vec3f_neg(relativePos));
78638}
78639
78640MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z)
78641{
78642 if (pSound == NULL) {
78643 return;
78644 }
78645
78647}
78648
78649MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound)
78650{
78651 if (pSound == NULL) {
78652 return ma_vec3f_init_3f(0, 0, 0);
78653 }
78654
78656}
78657
78658MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z)
78659{
78660 if (pSound == NULL) {
78661 return;
78662 }
78663
78665}
78666
78667MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound)
78668{
78669 if (pSound == NULL) {
78670 return ma_vec3f_init_3f(0, 0, 0);
78671 }
78672
78674}
78675
78676MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z)
78677{
78678 if (pSound == NULL) {
78679 return;
78680 }
78681
78683}
78684
78685MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound)
78686{
78687 if (pSound == NULL) {
78688 return ma_vec3f_init_3f(0, 0, 0);
78689 }
78690
78692}
78693
78695{
78696 if (pSound == NULL) {
78697 return;
78698 }
78699
78701}
78702
78704{
78705 if (pSound == NULL) {
78707 }
78708
78710}
78711
78712MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning)
78713{
78714 if (pSound == NULL) {
78715 return;
78716 }
78717
78719}
78720
78722{
78723 if (pSound == NULL) {
78725 }
78726
78728}
78729
78730MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff)
78731{
78732 if (pSound == NULL) {
78733 return;
78734 }
78735
78737}
78738
78739MA_API float ma_sound_get_rolloff(const ma_sound* pSound)
78740{
78741 if (pSound == NULL) {
78742 return 0;
78743 }
78744
78746}
78747
78748MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain)
78749{
78750 if (pSound == NULL) {
78751 return;
78752 }
78753
78755}
78756
78757MA_API float ma_sound_get_min_gain(const ma_sound* pSound)
78758{
78759 if (pSound == NULL) {
78760 return 0;
78761 }
78762
78764}
78765
78766MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain)
78767{
78768 if (pSound == NULL) {
78769 return;
78770 }
78771
78773}
78774
78775MA_API float ma_sound_get_max_gain(const ma_sound* pSound)
78776{
78777 if (pSound == NULL) {
78778 return 0;
78779 }
78780
78782}
78783
78784MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance)
78785{
78786 if (pSound == NULL) {
78787 return;
78788 }
78789
78791}
78792
78793MA_API float ma_sound_get_min_distance(const ma_sound* pSound)
78794{
78795 if (pSound == NULL) {
78796 return 0;
78797 }
78798
78800}
78801
78802MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance)
78803{
78804 if (pSound == NULL) {
78805 return;
78806 }
78807
78809}
78810
78811MA_API float ma_sound_get_max_distance(const ma_sound* pSound)
78812{
78813 if (pSound == NULL) {
78814 return 0;
78815 }
78816
78818}
78819
78820MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
78821{
78822 if (pSound == NULL) {
78823 return;
78824 }
78825
78826 ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain);
78827}
78828
78829MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
78830{
78831 if (pInnerAngleInRadians != NULL) {
78832 *pInnerAngleInRadians = 0;
78833 }
78834
78835 if (pOuterAngleInRadians != NULL) {
78836 *pOuterAngleInRadians = 0;
78837 }
78838
78839 if (pOuterGain != NULL) {
78840 *pOuterGain = 0;
78841 }
78842
78843 if (pSound == NULL) {
78844 return;
78845 }
78846
78847 ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
78848}
78849
78850MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor)
78851{
78852 if (pSound == NULL) {
78853 return;
78854 }
78855
78857}
78858
78859MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound)
78860{
78861 if (pSound == NULL) {
78862 return 0;
78863 }
78864
78866}
78867
78868MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor)
78869{
78870 if (pSound == NULL) {
78871 return;
78872 }
78873
78874 ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor);
78875}
78876
78878{
78879 if (pSound == NULL) {
78880 return 1;
78881 }
78882
78884}
78885
78886
78887MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)
78888{
78889 if (pSound == NULL) {
78890 return;
78891 }
78892
78893 ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, fadeLengthInFrames, (~(ma_uint64)0));
78894}
78895
78896MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)
78897{
78898 if (pSound == NULL) {
78899 return;
78900 }
78901
78902 ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000);
78903}
78904
78905MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames)
78906{
78907 if (pSound == NULL) {
78908 return;
78909 }
78910
78911 /*
78912 We don't want to update the fader at this point because we need to use the engine's current time
78913 to derive the fader's start offset. The timer is being updated on the audio thread so in order to
78914 do this as accurately as possible we'll need to defer this to the audio thread.
78915 */
78916 ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeBeg, volumeBeg);
78917 ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeEnd, volumeEnd);
78918 ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.fadeLengthInFrames, fadeLengthInFrames);
78919 ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.absoluteGlobalTimeInFrames, absoluteGlobalTimeInFrames);
78920}
78921
78922MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds)
78923{
78924 ma_uint32 sampleRate;
78925
78926 if (pSound == NULL) {
78927 return;
78928 }
78929
78930 sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound));
78931
78932 ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * sampleRate) / 1000, (absoluteGlobalTimeInMilliseconds * sampleRate) / 1000);
78933}
78934
78936{
78937 if (pSound == NULL) {
78938 return MA_INVALID_ARGS;
78939 }
78940
78942}
78943
78944MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames)
78945{
78946 if (pSound == NULL) {
78947 return;
78948 }
78949
78950 ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames);
78951}
78952
78953MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)
78954{
78955 if (pSound == NULL) {
78956 return;
78957 }
78958
78959 ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000);
78960}
78961
78962MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames)
78963{
78964 if (pSound == NULL) {
78965 return;
78966 }
78967
78968 ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, absoluteGlobalTimeInFrames, 0);
78969}
78970
78971MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)
78972{
78973 if (pSound == NULL) {
78974 return;
78975 }
78976
78977 ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000);
78978}
78979
78980MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames)
78981{
78982 if (pSound == NULL) {
78983 return;
78984 }
78985
78986 if (fadeLengthInFrames > 0) {
78987 if (fadeLengthInFrames > stopAbsoluteGlobalTimeInFrames) {
78988 fadeLengthInFrames = stopAbsoluteGlobalTimeInFrames;
78989 }
78990
78991 ma_sound_set_fade_start_in_pcm_frames(pSound, -1, 0, fadeLengthInFrames, stopAbsoluteGlobalTimeInFrames - fadeLengthInFrames);
78992 }
78993
78994 ma_node_set_state_time(pSound, ma_node_state_stopped, stopAbsoluteGlobalTimeInFrames);
78995}
78996
78997MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds)
78998{
78999 ma_uint32 sampleRate;
79000
79001 if (pSound == NULL) {
79002 return;
79003 }
79004
79005 sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound));
79006
79007 ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, (stopAbsoluteGlobalTimeInMilliseconds * sampleRate) / 1000, (fadeLengthInMilliseconds * sampleRate) / 1000);
79008}
79009
79011{
79012 if (pSound == NULL) {
79013 return MA_FALSE;
79014 }
79015
79017}
79018
79020{
79021 if (pSound == NULL) {
79022 return 0;
79023 }
79024
79025 return ma_node_get_time(pSound);
79026}
79027
79029{
79031 if (sampleRate == 0) {
79032 return 0; /* Prevent a division by zero. */
79033 }
79034
79035 return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / sampleRate;
79036}
79037
79038MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping)
79039{
79040 if (pSound == NULL) {
79041 return;
79042 }
79043
79044 /* Looping is only a valid concept if the sound is backed by a data source. */
79045 if (pSound->pDataSource == NULL) {
79046 return;
79047 }
79048
79049 /* The looping state needs to be applied to the data source in order for any looping to actually happen. */
79050 ma_data_source_set_looping(pSound->pDataSource, isLooping);
79051}
79052
79054{
79055 if (pSound == NULL) {
79056 return MA_FALSE;
79057 }
79058
79059 /* There is no notion of looping for sounds that are not backed by a data source. */
79060 if (pSound->pDataSource == NULL) {
79061 return MA_FALSE;
79062 }
79063
79064 return ma_data_source_is_looping(pSound->pDataSource);
79065}
79066
79068{
79069 if (pSound == NULL) {
79070 return MA_FALSE;
79071 }
79072
79073 /* There is no notion of an end of a sound if it's not backed by a data source. */
79074 if (pSound->pDataSource == NULL) {
79075 return MA_FALSE;
79076 }
79077
79078 return ma_sound_get_at_end(pSound);
79079}
79080
79082{
79083 if (pSound == NULL) {
79084 return MA_INVALID_ARGS;
79085 }
79086
79087 /* Seeking is only valid for sounds that are backed by a data source. */
79088 if (pSound->pDataSource == NULL) {
79089 return MA_INVALID_OPERATION;
79090 }
79091
79092 /* 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. */
79093 ma_atomic_exchange_64(&pSound->seekTarget, frameIndex);
79094
79095 return MA_SUCCESS;
79096}
79097
79098MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds)
79099{
79100 ma_uint64 frameIndex;
79101 ma_uint32 sampleRate;
79102 ma_result result;
79103
79104 if (pSound == NULL) {
79105 return MA_INVALID_ARGS;
79106 }
79107
79108 result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0);
79109 if (result != MA_SUCCESS) {
79110 return result;
79111 }
79112
79113 /* We need PCM frames. We need to convert first */
79114 frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate);
79115
79116 return ma_sound_seek_to_pcm_frame(pSound, frameIndex);
79117}
79118
79119MA_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)
79120{
79121 if (pSound == NULL) {
79122 return MA_INVALID_ARGS;
79123 }
79124
79125 /* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */
79126 if (pSound->pDataSource == NULL) {
79127 ma_uint32 channels;
79128
79129 if (pFormat != NULL) {
79130 *pFormat = ma_format_f32;
79131 }
79132
79133 channels = ma_node_get_input_channels(&pSound->engineNode, 0);
79134 if (pChannels != NULL) {
79135 *pChannels = channels;
79136 }
79137
79138 if (pSampleRate != NULL) {
79139 *pSampleRate = pSound->engineNode.resampler.config.sampleRateIn;
79140 }
79141
79142 if (pChannelMap != NULL) {
79143 ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels);
79144 }
79145
79146 return MA_SUCCESS;
79147 } else {
79148 return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
79149 }
79150}
79151
79153{
79154 ma_uint64 seekTarget;
79155
79156 if (pSound == NULL) {
79157 return MA_INVALID_ARGS;
79158 }
79159
79160 /* The notion of a cursor is only valid for sounds that are backed by a data source. */
79161 if (pSound->pDataSource == NULL) {
79162 return MA_INVALID_OPERATION;
79163 }
79164
79165 seekTarget = ma_atomic_load_64(&pSound->seekTarget);
79166 if (seekTarget != MA_SEEK_TARGET_NONE) {
79167 *pCursor = seekTarget;
79168 return MA_SUCCESS;
79169 } else {
79171 }
79172}
79173
79175{
79176 if (pSound == NULL) {
79177 return MA_INVALID_ARGS;
79178 }
79179
79180 /* The notion of a sound length is only valid for sounds that are backed by a data source. */
79181 if (pSound->pDataSource == NULL) {
79182 return MA_INVALID_OPERATION;
79183 }
79184
79186}
79187
79189{
79190 ma_result result;
79191 ma_uint64 cursorInPCMFrames;
79192 ma_uint32 sampleRate;
79193
79194 if (pCursor != NULL) {
79195 *pCursor = 0;
79196 }
79197
79198 result = ma_sound_get_cursor_in_pcm_frames(pSound, &cursorInPCMFrames);
79199 if (result != MA_SUCCESS) {
79200 return result;
79201 }
79202
79203 result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0);
79204 if (result != MA_SUCCESS) {
79205 return result;
79206 }
79207
79208 /* 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. */
79209 *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate;
79210
79211 return MA_SUCCESS;
79212}
79213
79215{
79216 if (pSound == NULL) {
79217 return MA_INVALID_ARGS;
79218 }
79219
79220 /* The notion of a sound length is only valid for sounds that are backed by a data source. */
79221 if (pSound->pDataSource == NULL) {
79222 return MA_INVALID_OPERATION;
79223 }
79224
79225 return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength);
79226}
79227
79228MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData)
79229{
79230 if (pSound == NULL) {
79231 return MA_INVALID_ARGS;
79232 }
79233
79234 /* The notion of an end is only valid for sounds that are backed by a data source. */
79235 if (pSound->pDataSource == NULL) {
79236 return MA_INVALID_OPERATION;
79237 }
79238
79239 pSound->endCallback = callback;
79240 pSound->pEndCallbackUserData = pUserData;
79241
79242 return MA_SUCCESS;
79243}
79244
79245
79247{
79249 config.flags = flags;
79250 config.pInitialAttachment = pParentGroup;
79251 return ma_sound_group_init_ex(pEngine, &config, pGroup);
79252}
79253
79255{
79256 ma_sound_config soundConfig;
79257
79258 if (pGroup == NULL) {
79259 return MA_INVALID_ARGS;
79260 }
79261
79262 MA_ZERO_OBJECT(pGroup);
79263
79264 if (pConfig == NULL) {
79265 return MA_INVALID_ARGS;
79266 }
79267
79268 /* A sound group is just a sound without a data source. */
79269 soundConfig = *pConfig;
79270 soundConfig.pFilePath = NULL;
79271 soundConfig.pFilePathW = NULL;
79272 soundConfig.pDataSource = NULL;
79273
79274 /*
79275 Groups need to have spatialization disabled by default because I think it'll be pretty rare
79276 that programs will want to spatialize groups (but not unheard of). Certainly it feels like
79277 disabling this by default feels like the right option. Spatialization can be enabled with a
79278 call to ma_sound_group_set_spatialization_enabled().
79279 */
79281
79282 return ma_sound_init_ex(pEngine, &soundConfig, pGroup);
79283}
79284
79286{
79287 ma_sound_uninit(pGroup);
79288}
79289
79291{
79292 return ma_sound_get_engine(pGroup);
79293}
79294
79296{
79297 return ma_sound_start(pGroup);
79298}
79299
79301{
79302 return ma_sound_stop(pGroup);
79303}
79304
79305MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume)
79306{
79307 ma_sound_set_volume(pGroup, volume);
79308}
79309
79311{
79312 return ma_sound_get_volume(pGroup);
79313}
79314
79315MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan)
79316{
79317 ma_sound_set_pan(pGroup, pan);
79318}
79319
79320MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup)
79321{
79322 return ma_sound_get_pan(pGroup);
79323}
79324
79326{
79327 ma_sound_set_pan_mode(pGroup, panMode);
79328}
79329
79331{
79332 return ma_sound_get_pan_mode(pGroup);
79333}
79334
79335MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch)
79336{
79337 ma_sound_set_pitch(pGroup, pitch);
79338}
79339
79341{
79342 return ma_sound_get_pitch(pGroup);
79343}
79344
79346{
79347 ma_sound_set_spatialization_enabled(pGroup, enabled);
79348}
79349
79351{
79353}
79354
79356{
79357 ma_sound_set_pinned_listener_index(pGroup, listenerIndex);
79358}
79359
79361{
79363}
79364
79366{
79367 return ma_sound_get_listener_index(pGroup);
79368}
79369
79371{
79373}
79374
79375MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z)
79376{
79377 ma_sound_set_position(pGroup, x, y, z);
79378}
79379
79381{
79382 return ma_sound_get_position(pGroup);
79383}
79384
79385MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z)
79386{
79387 ma_sound_set_direction(pGroup, x, y, z);
79388}
79389
79391{
79392 return ma_sound_get_direction(pGroup);
79393}
79394
79395MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z)
79396{
79397 ma_sound_set_velocity(pGroup, x, y, z);
79398}
79399
79401{
79402 return ma_sound_get_velocity(pGroup);
79403}
79404
79406{
79407 ma_sound_set_attenuation_model(pGroup, attenuationModel);
79408}
79409
79411{
79412 return ma_sound_get_attenuation_model(pGroup);
79413}
79414
79416{
79417 ma_sound_set_positioning(pGroup, positioning);
79418}
79419
79421{
79422 return ma_sound_get_positioning(pGroup);
79423}
79424
79425MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff)
79426{
79427 ma_sound_set_rolloff(pGroup, rolloff);
79428}
79429
79431{
79432 return ma_sound_get_rolloff(pGroup);
79433}
79434
79435MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain)
79436{
79437 ma_sound_set_min_gain(pGroup, minGain);
79438}
79439
79441{
79442 return ma_sound_get_min_gain(pGroup);
79443}
79444
79445MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain)
79446{
79447 ma_sound_set_max_gain(pGroup, maxGain);
79448}
79449
79451{
79452 return ma_sound_get_max_gain(pGroup);
79453}
79454
79455MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance)
79456{
79457 ma_sound_set_min_distance(pGroup, minDistance);
79458}
79459
79461{
79462 return ma_sound_get_min_distance(pGroup);
79463}
79464
79465MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance)
79466{
79467 ma_sound_set_max_distance(pGroup, maxDistance);
79468}
79469
79471{
79472 return ma_sound_get_max_distance(pGroup);
79473}
79474
79475MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
79476{
79477 ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain);
79478}
79479
79480MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
79481{
79482 ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
79483}
79484
79485MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor)
79486{
79487 ma_sound_set_doppler_factor(pGroup, dopplerFactor);
79488}
79489
79491{
79492 return ma_sound_get_doppler_factor(pGroup);
79493}
79494
79495MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor)
79496{
79497 ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor);
79498}
79499
79501{
79503}
79504
79505MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)
79506{
79507 ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames);
79508}
79509
79510MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)
79511{
79512 ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds);
79513}
79514
79516{
79517 return ma_sound_get_current_fade_volume(pGroup);
79518}
79519
79520MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames)
79521{
79522 ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames);
79523}
79524
79525MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)
79526{
79527 ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds);
79528}
79529
79530MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames)
79531{
79532 ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames);
79533}
79534
79535MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)
79536{
79537 ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds);
79538}
79539
79541{
79542 return ma_sound_is_playing(pGroup);
79543}
79544
79546{
79547 return ma_sound_get_time_in_pcm_frames(pGroup);
79548}
79549#endif /* MA_NO_ENGINE */
79550/* END SECTION: miniaudio_engine.c */
79551
79552
79553
79554/**************************************************************************************************************************************************************
79555***************************************************************************************************************************************************************
79556
79557Auto Generated
79558==============
79559All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as ma_dr_wav, ma_dr_flac, etc. If you find a bug in the
79560code below please report the bug to the respective repository for the relevant project (probably dr_libs).
79561
79562***************************************************************************************************************************************************************
79563**************************************************************************************************************************************************************/
79564#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
79565#if !defined(MA_DR_WAV_IMPLEMENTATION)
79566/* dr_wav_c begin */
79567#ifndef ma_dr_wav_c
79568#define ma_dr_wav_c
79569#ifdef __MRC__
79570#pragma options opt off
79571#endif
79572#include <stdlib.h>
79573#include <string.h>
79574#include <limits.h>
79575#ifndef MA_DR_WAV_NO_STDIO
79576#include <stdio.h>
79577#ifndef MA_DR_WAV_NO_WCHAR
79578#include <wchar.h>
79579#endif
79580#endif
79581#ifndef MA_DR_WAV_ASSERT
79582#include <assert.h>
79583#define MA_DR_WAV_ASSERT(expression) assert(expression)
79584#endif
79585#ifndef MA_DR_WAV_MALLOC
79586#define MA_DR_WAV_MALLOC(sz) malloc((sz))
79587#endif
79588#ifndef MA_DR_WAV_REALLOC
79589#define MA_DR_WAV_REALLOC(p, sz) realloc((p), (sz))
79590#endif
79591#ifndef MA_DR_WAV_FREE
79592#define MA_DR_WAV_FREE(p) free((p))
79593#endif
79594#ifndef MA_DR_WAV_COPY_MEMORY
79595#define MA_DR_WAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
79596#endif
79597#ifndef MA_DR_WAV_ZERO_MEMORY
79598#define MA_DR_WAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
79599#endif
79600#ifndef MA_DR_WAV_ZERO_OBJECT
79601#define MA_DR_WAV_ZERO_OBJECT(p) MA_DR_WAV_ZERO_MEMORY((p), sizeof(*p))
79602#endif
79603#define ma_dr_wav_countof(x) (sizeof(x) / sizeof(x[0]))
79604#define ma_dr_wav_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
79605#define ma_dr_wav_min(a, b) (((a) < (b)) ? (a) : (b))
79606#define ma_dr_wav_max(a, b) (((a) > (b)) ? (a) : (b))
79607#define ma_dr_wav_clamp(x, lo, hi) (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x))))
79608#define ma_dr_wav_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset))
79609#define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE 32
79610#define MA_DR_WAV_INT64_MIN ((ma_int64) ((ma_uint64)0x80000000 << 32))
79611#define MA_DR_WAV_INT64_MAX ((ma_int64)(((ma_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF))
79612#if defined(_MSC_VER) && _MSC_VER >= 1400
79613 #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC
79614 #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC
79615 #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC
79616#elif defined(__clang__)
79617 #if defined(__has_builtin)
79618 #if __has_builtin(__builtin_bswap16)
79619 #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC
79620 #endif
79621 #if __has_builtin(__builtin_bswap32)
79622 #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC
79623 #endif
79624 #if __has_builtin(__builtin_bswap64)
79625 #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC
79626 #endif
79627 #endif
79628#elif defined(__GNUC__)
79629 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
79630 #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC
79631 #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC
79632 #endif
79633 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
79634 #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC
79635 #endif
79636#endif
79637MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)
79638{
79639 if (pMajor) {
79640 *pMajor = MA_DR_WAV_VERSION_MAJOR;
79641 }
79642 if (pMinor) {
79643 *pMinor = MA_DR_WAV_VERSION_MINOR;
79644 }
79645 if (pRevision) {
79646 *pRevision = MA_DR_WAV_VERSION_REVISION;
79647 }
79648}
79649MA_API const char* ma_dr_wav_version_string(void)
79650{
79651 return MA_DR_WAV_VERSION_STRING;
79652}
79653#ifndef MA_DR_WAV_MAX_SAMPLE_RATE
79654#define MA_DR_WAV_MAX_SAMPLE_RATE 384000
79655#endif
79656#ifndef MA_DR_WAV_MAX_CHANNELS
79657#define MA_DR_WAV_MAX_CHANNELS 256
79658#endif
79659#ifndef MA_DR_WAV_MAX_BITS_PER_SAMPLE
79660#define MA_DR_WAV_MAX_BITS_PER_SAMPLE 64
79661#endif
79662static const ma_uint8 ma_dr_wavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00};
79663static const ma_uint8 ma_dr_wavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
79664static const ma_uint8 ma_dr_wavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
79665static const ma_uint8 ma_dr_wavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
79666static const ma_uint8 ma_dr_wavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
79667static MA_INLINE int ma_dr_wav__is_little_endian(void)
79668{
79669#if defined(MA_X86) || defined(MA_X64)
79670 return MA_TRUE;
79671#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
79672 return MA_TRUE;
79673#else
79674 int n = 1;
79675 return (*(char*)&n) == 1;
79676#endif
79677}
79678static MA_INLINE void ma_dr_wav_bytes_to_guid(const ma_uint8* data, ma_uint8* guid)
79679{
79680 int i;
79681 for (i = 0; i < 16; ++i) {
79682 guid[i] = data[i];
79683 }
79684}
79685static MA_INLINE ma_uint16 ma_dr_wav__bswap16(ma_uint16 n)
79686{
79687#ifdef MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC
79688 #if defined(_MSC_VER)
79689 return _byteswap_ushort(n);
79690 #elif defined(__GNUC__) || defined(__clang__)
79691 return __builtin_bswap16(n);
79692 #else
79693 #error "This compiler does not support the byte swap intrinsic."
79694 #endif
79695#else
79696 return ((n & 0xFF00) >> 8) |
79697 ((n & 0x00FF) << 8);
79698#endif
79699}
79700static MA_INLINE ma_uint32 ma_dr_wav__bswap32(ma_uint32 n)
79701{
79702#ifdef MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC
79703 #if defined(_MSC_VER)
79704 return _byteswap_ulong(n);
79705 #elif defined(__GNUC__) || defined(__clang__)
79706 #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT)
79707 ma_uint32 r;
79708 __asm__ __volatile__ (
79709 #if defined(MA_64BIT)
79710 "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n)
79711 #else
79712 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
79713 #endif
79714 );
79715 return r;
79716 #else
79717 return __builtin_bswap32(n);
79718 #endif
79719 #else
79720 #error "This compiler does not support the byte swap intrinsic."
79721 #endif
79722#else
79723 return ((n & 0xFF000000) >> 24) |
79724 ((n & 0x00FF0000) >> 8) |
79725 ((n & 0x0000FF00) << 8) |
79726 ((n & 0x000000FF) << 24);
79727#endif
79728}
79729static MA_INLINE ma_uint64 ma_dr_wav__bswap64(ma_uint64 n)
79730{
79731#ifdef MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC
79732 #if defined(_MSC_VER)
79733 return _byteswap_uint64(n);
79734 #elif defined(__GNUC__) || defined(__clang__)
79735 return __builtin_bswap64(n);
79736 #else
79737 #error "This compiler does not support the byte swap intrinsic."
79738 #endif
79739#else
79740 return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) |
79741 ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) |
79742 ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) |
79743 ((n & ((ma_uint64)0x000000FF << 32)) >> 8) |
79744 ((n & ((ma_uint64)0xFF000000 )) << 8) |
79745 ((n & ((ma_uint64)0x00FF0000 )) << 24) |
79746 ((n & ((ma_uint64)0x0000FF00 )) << 40) |
79747 ((n & ((ma_uint64)0x000000FF )) << 56);
79748#endif
79749}
79750static MA_INLINE ma_int16 ma_dr_wav__bswap_s16(ma_int16 n)
79751{
79752 return (ma_int16)ma_dr_wav__bswap16((ma_uint16)n);
79753}
79754static MA_INLINE void ma_dr_wav__bswap_samples_s16(ma_int16* pSamples, ma_uint64 sampleCount)
79755{
79756 ma_uint64 iSample;
79757 for (iSample = 0; iSample < sampleCount; iSample += 1) {
79758 pSamples[iSample] = ma_dr_wav__bswap_s16(pSamples[iSample]);
79759 }
79760}
79761static MA_INLINE void ma_dr_wav__bswap_s24(ma_uint8* p)
79762{
79763 ma_uint8 t;
79764 t = p[0];
79765 p[0] = p[2];
79766 p[2] = t;
79767}
79768static MA_INLINE void ma_dr_wav__bswap_samples_s24(ma_uint8* pSamples, ma_uint64 sampleCount)
79769{
79770 ma_uint64 iSample;
79771 for (iSample = 0; iSample < sampleCount; iSample += 1) {
79772 ma_uint8* pSample = pSamples + (iSample*3);
79773 ma_dr_wav__bswap_s24(pSample);
79774 }
79775}
79776static MA_INLINE ma_int32 ma_dr_wav__bswap_s32(ma_int32 n)
79777{
79778 return (ma_int32)ma_dr_wav__bswap32((ma_uint32)n);
79779}
79780static MA_INLINE void ma_dr_wav__bswap_samples_s32(ma_int32* pSamples, ma_uint64 sampleCount)
79781{
79782 ma_uint64 iSample;
79783 for (iSample = 0; iSample < sampleCount; iSample += 1) {
79784 pSamples[iSample] = ma_dr_wav__bswap_s32(pSamples[iSample]);
79785 }
79786}
79787static MA_INLINE ma_int64 ma_dr_wav__bswap_s64(ma_int64 n)
79788{
79789 return (ma_int64)ma_dr_wav__bswap64((ma_uint64)n);
79790}
79791static MA_INLINE void ma_dr_wav__bswap_samples_s64(ma_int64* pSamples, ma_uint64 sampleCount)
79792{
79793 ma_uint64 iSample;
79794 for (iSample = 0; iSample < sampleCount; iSample += 1) {
79795 pSamples[iSample] = ma_dr_wav__bswap_s64(pSamples[iSample]);
79796 }
79797}
79798static MA_INLINE float ma_dr_wav__bswap_f32(float n)
79799{
79800 union {
79801 ma_uint32 i;
79802 float f;
79803 } x;
79804 x.f = n;
79805 x.i = ma_dr_wav__bswap32(x.i);
79806 return x.f;
79807}
79808static MA_INLINE void ma_dr_wav__bswap_samples_f32(float* pSamples, ma_uint64 sampleCount)
79809{
79810 ma_uint64 iSample;
79811 for (iSample = 0; iSample < sampleCount; iSample += 1) {
79812 pSamples[iSample] = ma_dr_wav__bswap_f32(pSamples[iSample]);
79813 }
79814}
79815static MA_INLINE void ma_dr_wav__bswap_samples(void* pSamples, ma_uint64 sampleCount, ma_uint32 bytesPerSample)
79816{
79817 switch (bytesPerSample)
79818 {
79819 case 1:
79820 {
79821 } break;
79822 case 2:
79823 {
79824 ma_dr_wav__bswap_samples_s16((ma_int16*)pSamples, sampleCount);
79825 } break;
79826 case 3:
79827 {
79828 ma_dr_wav__bswap_samples_s24((ma_uint8*)pSamples, sampleCount);
79829 } break;
79830 case 4:
79831 {
79832 ma_dr_wav__bswap_samples_s32((ma_int32*)pSamples, sampleCount);
79833 } break;
79834 case 8:
79835 {
79836 ma_dr_wav__bswap_samples_s64((ma_int64*)pSamples, sampleCount);
79837 } break;
79838 default:
79839 {
79840 MA_DR_WAV_ASSERT(MA_FALSE);
79841 } break;
79842 }
79843}
79844MA_PRIVATE MA_INLINE ma_bool32 ma_dr_wav_is_container_be(ma_dr_wav_container container)
79845{
79846 if (container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_aiff) {
79847 return MA_TRUE;
79848 } else {
79849 return MA_FALSE;
79850 }
79851}
79852MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_le(const ma_uint8* data)
79853{
79854 return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8);
79855}
79856MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_be(const ma_uint8* data)
79857{
79858 return ((ma_uint16)data[1] << 0) | ((ma_uint16)data[0] << 8);
79859}
79860MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_ex(const ma_uint8* data, ma_dr_wav_container container)
79861{
79862 if (ma_dr_wav_is_container_be(container)) {
79863 return ma_dr_wav_bytes_to_u16_be(data);
79864 } else {
79865 return ma_dr_wav_bytes_to_u16_le(data);
79866 }
79867}
79868MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_le(const ma_uint8* data)
79869{
79870 return ((ma_uint32)data[0] << 0) | ((ma_uint32)data[1] << 8) | ((ma_uint32)data[2] << 16) | ((ma_uint32)data[3] << 24);
79871}
79872MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_be(const ma_uint8* data)
79873{
79874 return ((ma_uint32)data[3] << 0) | ((ma_uint32)data[2] << 8) | ((ma_uint32)data[1] << 16) | ((ma_uint32)data[0] << 24);
79875}
79876MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_ex(const ma_uint8* data, ma_dr_wav_container container)
79877{
79878 if (ma_dr_wav_is_container_be(container)) {
79879 return ma_dr_wav_bytes_to_u32_be(data);
79880 } else {
79881 return ma_dr_wav_bytes_to_u32_le(data);
79882 }
79883}
79884MA_PRIVATE ma_int64 ma_dr_wav_aiff_extented_to_s64(const ma_uint8* data)
79885{
79886 ma_uint32 exponent = ((ma_uint32)data[0] << 8) | data[1];
79887 ma_uint64 hi = ((ma_uint64)data[2] << 24) | ((ma_uint64)data[3] << 16) | ((ma_uint64)data[4] << 8) | ((ma_uint64)data[5] << 0);
79888 ma_uint64 lo = ((ma_uint64)data[6] << 24) | ((ma_uint64)data[7] << 16) | ((ma_uint64)data[8] << 8) | ((ma_uint64)data[9] << 0);
79889 ma_uint64 significand = (hi << 32) | lo;
79890 int sign = exponent >> 15;
79891 exponent &= 0x7FFF;
79892 if (exponent == 0 && significand == 0) {
79893 return 0;
79894 } else if (exponent == 0x7FFF) {
79895 return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX;
79896 }
79897 exponent -= 16383;
79898 if (exponent > 63) {
79899 return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX;
79900 } else if (exponent < 1) {
79901 return 0;
79902 }
79903 significand >>= (63 - exponent);
79904 if (sign) {
79905 return -(ma_int64)significand;
79906 } else {
79907 return (ma_int64)significand;
79908 }
79909}
79910MA_PRIVATE void* ma_dr_wav__malloc_default(size_t sz, void* pUserData)
79911{
79912 (void)pUserData;
79913 return MA_DR_WAV_MALLOC(sz);
79914}
79915MA_PRIVATE void* ma_dr_wav__realloc_default(void* p, size_t sz, void* pUserData)
79916{
79917 (void)pUserData;
79918 return MA_DR_WAV_REALLOC(p, sz);
79919}
79920MA_PRIVATE void ma_dr_wav__free_default(void* p, void* pUserData)
79921{
79922 (void)pUserData;
79923 MA_DR_WAV_FREE(p);
79924}
79925MA_PRIVATE void* ma_dr_wav__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
79926{
79927 if (pAllocationCallbacks == NULL) {
79928 return NULL;
79929 }
79930 if (pAllocationCallbacks->onMalloc != NULL) {
79931 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
79932 }
79933 if (pAllocationCallbacks->onRealloc != NULL) {
79934 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
79935 }
79936 return NULL;
79937}
79938MA_PRIVATE void* ma_dr_wav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks)
79939{
79940 if (pAllocationCallbacks == NULL) {
79941 return NULL;
79942 }
79943 if (pAllocationCallbacks->onRealloc != NULL) {
79944 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
79945 }
79946 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
79947 void* p2;
79948 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
79949 if (p2 == NULL) {
79950 return NULL;
79951 }
79952 if (p != NULL) {
79953 MA_DR_WAV_COPY_MEMORY(p2, p, szOld);
79954 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
79955 }
79956 return p2;
79957 }
79958 return NULL;
79959}
79960MA_PRIVATE void ma_dr_wav__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
79961{
79962 if (p == NULL || pAllocationCallbacks == NULL) {
79963 return;
79964 }
79965 if (pAllocationCallbacks->onFree != NULL) {
79966 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
79967 }
79968}
79969MA_PRIVATE ma_allocation_callbacks ma_dr_wav_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks)
79970{
79971 if (pAllocationCallbacks != NULL) {
79972 return *pAllocationCallbacks;
79973 } else {
79974 ma_allocation_callbacks allocationCallbacks;
79975 allocationCallbacks.pUserData = NULL;
79976 allocationCallbacks.onMalloc = ma_dr_wav__malloc_default;
79977 allocationCallbacks.onRealloc = ma_dr_wav__realloc_default;
79978 allocationCallbacks.onFree = ma_dr_wav__free_default;
79979 return allocationCallbacks;
79980 }
79981}
79982static MA_INLINE ma_bool32 ma_dr_wav__is_compressed_format_tag(ma_uint16 formatTag)
79983{
79984 return
79985 formatTag == MA_DR_WAVE_FORMAT_ADPCM ||
79986 formatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM;
79987}
79988MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_riff(ma_uint64 chunkSize)
79989{
79990 return (unsigned int)(chunkSize % 2);
79991}
79992MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_w64(ma_uint64 chunkSize)
79993{
79994 return (unsigned int)(chunkSize % 8);
79995}
79996MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut);
79997MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut);
79998MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount);
79999MA_PRIVATE ma_result ma_dr_wav__read_chunk_header(ma_dr_wav_read_proc onRead, void* pUserData, ma_dr_wav_container container, ma_uint64* pRunningBytesReadOut, ma_dr_wav_chunk_header* pHeaderOut)
80000{
80001 if (container == ma_dr_wav_container_riff || container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_rf64 || container == ma_dr_wav_container_aiff) {
80002 ma_uint8 sizeInBytes[4];
80003 if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) {
80004 return MA_AT_END;
80005 }
80006 if (onRead(pUserData, sizeInBytes, 4) != 4) {
80007 return MA_INVALID_FILE;
80008 }
80009 pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u32_ex(sizeInBytes, container);
80010 pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_riff(pHeaderOut->sizeInBytes);
80011 *pRunningBytesReadOut += 8;
80012 } else if (container == ma_dr_wav_container_w64) {
80013 ma_uint8 sizeInBytes[8];
80014 if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) {
80015 return MA_AT_END;
80016 }
80017 if (onRead(pUserData, sizeInBytes, 8) != 8) {
80018 return MA_INVALID_FILE;
80019 }
80020 pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u64(sizeInBytes) - 24;
80021 pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_w64(pHeaderOut->sizeInBytes);
80022 *pRunningBytesReadOut += 24;
80023 } else {
80024 return MA_INVALID_FILE;
80025 }
80026 return MA_SUCCESS;
80027}
80028MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData)
80029{
80030 ma_uint64 bytesRemainingToSeek = offset;
80031 while (bytesRemainingToSeek > 0) {
80032 if (bytesRemainingToSeek > 0x7FFFFFFF) {
80033 if (!onSeek(pUserData, 0x7FFFFFFF, MA_DR_WAV_SEEK_CUR)) {
80034 return MA_FALSE;
80035 }
80036 bytesRemainingToSeek -= 0x7FFFFFFF;
80037 } else {
80038 if (!onSeek(pUserData, (int)bytesRemainingToSeek, MA_DR_WAV_SEEK_CUR)) {
80039 return MA_FALSE;
80040 }
80041 bytesRemainingToSeek = 0;
80042 }
80043 }
80044 return MA_TRUE;
80045}
80046MA_PRIVATE ma_bool32 ma_dr_wav__seek_from_start(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData)
80047{
80048 if (offset <= 0x7FFFFFFF) {
80049 return onSeek(pUserData, (int)offset, MA_DR_WAV_SEEK_SET);
80050 }
80051 if (!onSeek(pUserData, 0x7FFFFFFF, MA_DR_WAV_SEEK_SET)) {
80052 return MA_FALSE;
80053 }
80054 offset -= 0x7FFFFFFF;
80055 for (;;) {
80056 if (offset <= 0x7FFFFFFF) {
80057 return onSeek(pUserData, (int)offset, MA_DR_WAV_SEEK_CUR);
80058 }
80059 if (!onSeek(pUserData, 0x7FFFFFFF, MA_DR_WAV_SEEK_CUR)) {
80060 return MA_FALSE;
80061 }
80062 offset -= 0x7FFFFFFF;
80063 }
80064}
80065MA_PRIVATE size_t ma_dr_wav__on_read(ma_dr_wav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor)
80066{
80067 size_t bytesRead;
80068 MA_DR_WAV_ASSERT(onRead != NULL);
80069 MA_DR_WAV_ASSERT(pCursor != NULL);
80070 bytesRead = onRead(pUserData, pBufferOut, bytesToRead);
80071 *pCursor += bytesRead;
80072 return bytesRead;
80073}
80074#if 0
80075MA_PRIVATE ma_bool32 ma_dr_wav__on_seek(ma_dr_wav_seek_proc onSeek, void* pUserData, int offset, ma_dr_wav_seek_origin origin, ma_uint64* pCursor)
80076{
80077 MA_DR_WAV_ASSERT(onSeek != NULL);
80078 MA_DR_WAV_ASSERT(pCursor != NULL);
80079 if (!onSeek(pUserData, offset, origin)) {
80080 return MA_FALSE;
80081 }
80082 if (origin == MA_DR_WAV_SEEK_SET) {
80083 *pCursor = offset;
80084 } else {
80085 *pCursor += offset;
80086 }
80087 return MA_TRUE;
80088}
80089#endif
80090#define MA_DR_WAV_SMPL_BYTES 36
80091#define MA_DR_WAV_SMPL_LOOP_BYTES 24
80092#define MA_DR_WAV_INST_BYTES 7
80093#define MA_DR_WAV_ACID_BYTES 24
80094#define MA_DR_WAV_CUE_BYTES 4
80095#define MA_DR_WAV_BEXT_BYTES 602
80096#define MA_DR_WAV_BEXT_DESCRIPTION_BYTES 256
80097#define MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES 32
80098#define MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES 32
80099#define MA_DR_WAV_BEXT_RESERVED_BYTES 180
80100#define MA_DR_WAV_BEXT_UMID_BYTES 64
80101#define MA_DR_WAV_CUE_POINT_BYTES 24
80102#define MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES 4
80103#define MA_DR_WAV_LIST_LABELLED_TEXT_BYTES 20
80104#define MA_DR_WAV_METADATA_ALIGNMENT 8
80105typedef enum
80106{
80107 ma_dr_wav__metadata_parser_stage_count,
80108 ma_dr_wav__metadata_parser_stage_read
80109} ma_dr_wav__metadata_parser_stage;
80110typedef struct
80111{
80112 ma_dr_wav_read_proc onRead;
80113 ma_dr_wav_seek_proc onSeek;
80114 void *pReadSeekUserData;
80115 ma_dr_wav__metadata_parser_stage stage;
80116 ma_dr_wav_metadata *pMetadata;
80117 ma_uint32 metadataCount;
80118 ma_uint8 *pData;
80119 ma_uint8 *pDataCursor;
80120 ma_uint64 metadataCursor;
80121 ma_uint64 extraCapacity;
80122} ma_dr_wav__metadata_parser;
80123MA_PRIVATE size_t ma_dr_wav__metadata_memory_capacity(ma_dr_wav__metadata_parser* pParser)
80124{
80125 ma_uint64 cap = sizeof(ma_dr_wav_metadata) * (ma_uint64)pParser->metadataCount + pParser->extraCapacity;
80126 if (cap > MA_SIZE_MAX) {
80127 return 0;
80128 }
80129 return (size_t)cap;
80130}
80131MA_PRIVATE ma_uint8* ma_dr_wav__metadata_get_memory(ma_dr_wav__metadata_parser* pParser, size_t size, size_t align)
80132{
80133 ma_uint8* pResult;
80134 if (align) {
80135 ma_uintptr modulo = (ma_uintptr)pParser->pDataCursor % align;
80136 if (modulo != 0) {
80137 pParser->pDataCursor += align - modulo;
80138 }
80139 }
80140 pResult = pParser->pDataCursor;
80141 MA_DR_WAV_ASSERT((pResult + size) <= (pParser->pData + ma_dr_wav__metadata_memory_capacity(pParser)));
80142 pParser->pDataCursor += size;
80143 return pResult;
80144}
80145MA_PRIVATE void ma_dr_wav__metadata_request_extra_memory_for_stage_2(ma_dr_wav__metadata_parser* pParser, size_t bytes, size_t align)
80146{
80147 size_t extra = bytes + (align ? (align - 1) : 0);
80148 pParser->extraCapacity += extra;
80149}
80150MA_PRIVATE ma_result ma_dr_wav__metadata_alloc(ma_dr_wav__metadata_parser* pParser, ma_allocation_callbacks* pAllocationCallbacks)
80151{
80152 if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) {
80153 pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData);
80154 pParser->pData = (ma_uint8*)pAllocationCallbacks->onMalloc(ma_dr_wav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData);
80155 pParser->pDataCursor = pParser->pData;
80156 if (pParser->pData == NULL) {
80157 return MA_OUT_OF_MEMORY;
80158 }
80159 pParser->pMetadata = (ma_dr_wav_metadata*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_metadata) * pParser->metadataCount, 1);
80160 pParser->metadataCursor = 0;
80161 }
80162 return MA_SUCCESS;
80163}
80164MA_PRIVATE size_t ma_dr_wav__metadata_parser_read(ma_dr_wav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor)
80165{
80166 if (pCursor != NULL) {
80167 return ma_dr_wav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor);
80168 } else {
80169 return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead);
80170 }
80171}
80172MA_PRIVATE ma_uint64 ma_dr_wav__read_smpl_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata)
80173{
80174 ma_uint8 smplHeaderData[MA_DR_WAV_SMPL_BYTES];
80175 ma_uint64 totalBytesRead = 0;
80176 size_t bytesJustRead;
80177 if (pMetadata == NULL) {
80178 return 0;
80179 }
80180 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead);
80181 MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
80182 MA_DR_WAV_ASSERT(pChunkHeader != NULL);
80183 if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) {
80184 ma_uint32 iSampleLoop;
80185 pMetadata->type = ma_dr_wav_metadata_type_smpl;
80186 pMetadata->data.smpl.manufacturerId = ma_dr_wav_bytes_to_u32(smplHeaderData + 0);
80187 pMetadata->data.smpl.productId = ma_dr_wav_bytes_to_u32(smplHeaderData + 4);
80188 pMetadata->data.smpl.samplePeriodNanoseconds = ma_dr_wav_bytes_to_u32(smplHeaderData + 8);
80189 pMetadata->data.smpl.midiUnityNote = ma_dr_wav_bytes_to_u32(smplHeaderData + 12);
80190 pMetadata->data.smpl.midiPitchFraction = ma_dr_wav_bytes_to_u32(smplHeaderData + 16);
80191 pMetadata->data.smpl.smpteFormat = ma_dr_wav_bytes_to_u32(smplHeaderData + 20);
80192 pMetadata->data.smpl.smpteOffset = ma_dr_wav_bytes_to_u32(smplHeaderData + 24);
80193 pMetadata->data.smpl.sampleLoopCount = ma_dr_wav_bytes_to_u32(smplHeaderData + 28);
80194 pMetadata->data.smpl.samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(smplHeaderData + 32);
80195 if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES) {
80196 pMetadata->data.smpl.pLoops = (ma_dr_wav_smpl_loop*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, MA_DR_WAV_METADATA_ALIGNMENT);
80197 for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) {
80198 ma_uint8 smplLoopData[MA_DR_WAV_SMPL_LOOP_BYTES];
80199 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead);
80200 if (bytesJustRead == sizeof(smplLoopData)) {
80201 pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = ma_dr_wav_bytes_to_u32(smplLoopData + 0);
80202 pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4);
80203 pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8);
80204 pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12);
80205 pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16);
80206 pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20);
80207 } else {
80208 break;
80209 }
80210 }
80211 if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {
80212 pMetadata->data.smpl.pSamplerSpecificData = ma_dr_wav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1);
80213 MA_DR_WAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL);
80214 ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead);
80215 }
80216 }
80217 }
80218 return totalBytesRead;
80219}
80220MA_PRIVATE ma_uint64 ma_dr_wav__read_cue_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata)
80221{
80222 ma_uint8 cueHeaderSectionData[MA_DR_WAV_CUE_BYTES];
80223 ma_uint64 totalBytesRead = 0;
80224 size_t bytesJustRead;
80225 if (pMetadata == NULL) {
80226 return 0;
80227 }
80228 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead);
80229 MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
80230 if (bytesJustRead == sizeof(cueHeaderSectionData)) {
80231 pMetadata->type = ma_dr_wav_metadata_type_cue;
80232 pMetadata->data.cue.cuePointCount = ma_dr_wav_bytes_to_u32(cueHeaderSectionData);
80233 if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES) {
80234 pMetadata->data.cue.pCuePoints = (ma_dr_wav_cue_point*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_cue_point) * pMetadata->data.cue.cuePointCount, MA_DR_WAV_METADATA_ALIGNMENT);
80235 MA_DR_WAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL);
80236 if (pMetadata->data.cue.cuePointCount > 0) {
80237 ma_uint32 iCuePoint;
80238 for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {
80239 ma_uint8 cuePointData[MA_DR_WAV_CUE_POINT_BYTES];
80240 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead);
80241 if (bytesJustRead == sizeof(cuePointData)) {
80242 pMetadata->data.cue.pCuePoints[iCuePoint].id = ma_dr_wav_bytes_to_u32(cuePointData + 0);
80243 pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = ma_dr_wav_bytes_to_u32(cuePointData + 4);
80244 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8];
80245 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9];
80246 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10];
80247 pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11];
80248 pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = ma_dr_wav_bytes_to_u32(cuePointData + 12);
80249 pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = ma_dr_wav_bytes_to_u32(cuePointData + 16);
80250 pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20);
80251 } else {
80252 break;
80253 }
80254 }
80255 }
80256 }
80257 }
80258 return totalBytesRead;
80259}
80260MA_PRIVATE ma_uint64 ma_dr_wav__read_inst_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata)
80261{
80262 ma_uint8 instData[MA_DR_WAV_INST_BYTES];
80263 ma_uint64 bytesRead;
80264 if (pMetadata == NULL) {
80265 return 0;
80266 }
80267 bytesRead = ma_dr_wav__metadata_parser_read(pParser, instData, sizeof(instData), NULL);
80268 MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
80269 if (bytesRead == sizeof(instData)) {
80270 pMetadata->type = ma_dr_wav_metadata_type_inst;
80271 pMetadata->data.inst.midiUnityNote = (ma_int8)instData[0];
80272 pMetadata->data.inst.fineTuneCents = (ma_int8)instData[1];
80273 pMetadata->data.inst.gainDecibels = (ma_int8)instData[2];
80274 pMetadata->data.inst.lowNote = (ma_int8)instData[3];
80275 pMetadata->data.inst.highNote = (ma_int8)instData[4];
80276 pMetadata->data.inst.lowVelocity = (ma_int8)instData[5];
80277 pMetadata->data.inst.highVelocity = (ma_int8)instData[6];
80278 }
80279 return bytesRead;
80280}
80281MA_PRIVATE ma_uint64 ma_dr_wav__read_acid_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata)
80282{
80283 ma_uint8 acidData[MA_DR_WAV_ACID_BYTES];
80284 ma_uint64 bytesRead;
80285 if (pMetadata == NULL) {
80286 return 0;
80287 }
80288 bytesRead = ma_dr_wav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL);
80289 MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
80290 if (bytesRead == sizeof(acidData)) {
80291 pMetadata->type = ma_dr_wav_metadata_type_acid;
80292 pMetadata->data.acid.flags = ma_dr_wav_bytes_to_u32(acidData + 0);
80293 pMetadata->data.acid.midiUnityNote = ma_dr_wav_bytes_to_u16(acidData + 4);
80294 pMetadata->data.acid.reserved1 = ma_dr_wav_bytes_to_u16(acidData + 6);
80295 pMetadata->data.acid.reserved2 = ma_dr_wav_bytes_to_f32(acidData + 8);
80296 pMetadata->data.acid.numBeats = ma_dr_wav_bytes_to_u32(acidData + 12);
80297 pMetadata->data.acid.meterDenominator = ma_dr_wav_bytes_to_u16(acidData + 16);
80298 pMetadata->data.acid.meterNumerator = ma_dr_wav_bytes_to_u16(acidData + 18);
80299 pMetadata->data.acid.tempo = ma_dr_wav_bytes_to_f32(acidData + 20);
80300 }
80301 return bytesRead;
80302}
80303MA_PRIVATE size_t ma_dr_wav__strlen(const char* str)
80304{
80305 size_t result = 0;
80306 while (*str++) {
80307 result += 1;
80308 }
80309 return result;
80310}
80311MA_PRIVATE size_t ma_dr_wav__strlen_clamped(const char* str, size_t maxToRead)
80312{
80313 size_t result = 0;
80314 while (*str++ && result < maxToRead) {
80315 result += 1;
80316 }
80317 return result;
80318}
80319MA_PRIVATE char* ma_dr_wav__metadata_copy_string(ma_dr_wav__metadata_parser* pParser, const char* str, size_t maxToRead)
80320{
80321 size_t len = ma_dr_wav__strlen_clamped(str, maxToRead);
80322 if (len) {
80323 char* result = (char*)ma_dr_wav__metadata_get_memory(pParser, len + 1, 1);
80324 MA_DR_WAV_ASSERT(result != NULL);
80325 MA_DR_WAV_COPY_MEMORY(result, str, len);
80326 result[len] = '\0';
80327 return result;
80328 } else {
80329 return NULL;
80330 }
80331}
80332typedef struct
80333{
80334 const void* pBuffer;
80335 size_t sizeInBytes;
80336 size_t cursor;
80337} ma_dr_wav_buffer_reader;
80338MA_PRIVATE ma_result ma_dr_wav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, ma_dr_wav_buffer_reader* pReader)
80339{
80340 MA_DR_WAV_ASSERT(pBuffer != NULL);
80341 MA_DR_WAV_ASSERT(pReader != NULL);
80342 MA_DR_WAV_ZERO_OBJECT(pReader);
80343 pReader->pBuffer = pBuffer;
80344 pReader->sizeInBytes = sizeInBytes;
80345 pReader->cursor = 0;
80346 return MA_SUCCESS;
80347}
80348MA_PRIVATE const void* ma_dr_wav_buffer_reader_ptr(const ma_dr_wav_buffer_reader* pReader)
80349{
80350 MA_DR_WAV_ASSERT(pReader != NULL);
80351 return ma_dr_wav_offset_ptr(pReader->pBuffer, pReader->cursor);
80352}
80353MA_PRIVATE ma_result ma_dr_wav_buffer_reader_seek(ma_dr_wav_buffer_reader* pReader, size_t bytesToSeek)
80354{
80355 MA_DR_WAV_ASSERT(pReader != NULL);
80356 if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) {
80357 return MA_BAD_SEEK;
80358 }
80359 pReader->cursor += bytesToSeek;
80360 return MA_SUCCESS;
80361}
80362MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read(ma_dr_wav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead)
80363{
80364 ma_result result = MA_SUCCESS;
80365 size_t bytesRemaining;
80366 MA_DR_WAV_ASSERT(pReader != NULL);
80367 if (pBytesRead != NULL) {
80368 *pBytesRead = 0;
80369 }
80370 bytesRemaining = (pReader->sizeInBytes - pReader->cursor);
80371 if (bytesToRead > bytesRemaining) {
80372 bytesToRead = bytesRemaining;
80373 }
80374 if (pDst == NULL) {
80375 result = ma_dr_wav_buffer_reader_seek(pReader, bytesToRead);
80376 } else {
80377 MA_DR_WAV_COPY_MEMORY(pDst, ma_dr_wav_buffer_reader_ptr(pReader), bytesToRead);
80378 pReader->cursor += bytesToRead;
80379 }
80380 MA_DR_WAV_ASSERT(pReader->cursor <= pReader->sizeInBytes);
80381 if (result == MA_SUCCESS) {
80382 if (pBytesRead != NULL) {
80383 *pBytesRead = bytesToRead;
80384 }
80385 }
80386 return MA_SUCCESS;
80387}
80388MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u16(ma_dr_wav_buffer_reader* pReader, ma_uint16* pDst)
80389{
80390 ma_result result;
80391 size_t bytesRead;
80392 ma_uint8 data[2];
80393 MA_DR_WAV_ASSERT(pReader != NULL);
80394 MA_DR_WAV_ASSERT(pDst != NULL);
80395 *pDst = 0;
80396 result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead);
80397 if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) {
80398 return result;
80399 }
80400 *pDst = ma_dr_wav_bytes_to_u16(data);
80401 return MA_SUCCESS;
80402}
80403MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u32(ma_dr_wav_buffer_reader* pReader, ma_uint32* pDst)
80404{
80405 ma_result result;
80406 size_t bytesRead;
80407 ma_uint8 data[4];
80408 MA_DR_WAV_ASSERT(pReader != NULL);
80409 MA_DR_WAV_ASSERT(pDst != NULL);
80410 *pDst = 0;
80411 result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead);
80412 if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) {
80413 return result;
80414 }
80415 *pDst = ma_dr_wav_bytes_to_u32(data);
80416 return MA_SUCCESS;
80417}
80418MA_PRIVATE ma_uint64 ma_dr_wav__read_bext_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize)
80419{
80420 ma_uint8 bextData[MA_DR_WAV_BEXT_BYTES];
80421 size_t bytesRead = ma_dr_wav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL);
80422 MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
80423 if (bytesRead == sizeof(bextData)) {
80424 ma_dr_wav_buffer_reader reader;
80425 ma_uint32 timeReferenceLow;
80426 ma_uint32 timeReferenceHigh;
80427 size_t extraBytes;
80428 pMetadata->type = ma_dr_wav_metadata_type_bext;
80429 if (ma_dr_wav_buffer_reader_init(bextData, bytesRead, &reader) == MA_SUCCESS) {
80430 pMetadata->data.bext.pDescription = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_DESCRIPTION_BYTES);
80431 ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_DESCRIPTION_BYTES);
80432 pMetadata->data.bext.pOriginatorName = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES);
80433 ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES);
80434 pMetadata->data.bext.pOriginatorReference = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES);
80435 ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES);
80436 ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL);
80437 ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL);
80438 ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceLow);
80439 ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceHigh);
80440 pMetadata->data.bext.timeReference = ((ma_uint64)timeReferenceHigh << 32) + timeReferenceLow;
80441 ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version);
80442 pMetadata->data.bext.pUMID = ma_dr_wav__metadata_get_memory(pParser, MA_DR_WAV_BEXT_UMID_BYTES, 1);
80443 ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES, NULL);
80444 ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue);
80445 ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange);
80446 ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel);
80447 ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness);
80448 ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness);
80449 MA_DR_WAV_ASSERT((ma_dr_wav_offset_ptr(ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_RESERVED_BYTES)) == (bextData + MA_DR_WAV_BEXT_BYTES));
80450 extraBytes = (size_t)(chunkSize - MA_DR_WAV_BEXT_BYTES);
80451 if (extraBytes > 0) {
80452 pMetadata->data.bext.pCodingHistory = (char*)ma_dr_wav__metadata_get_memory(pParser, extraBytes + 1, 1);
80453 MA_DR_WAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL);
80454 bytesRead += ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL);
80455 pMetadata->data.bext.codingHistorySize = (ma_uint32)ma_dr_wav__strlen(pMetadata->data.bext.pCodingHistory);
80456 } else {
80457 pMetadata->data.bext.pCodingHistory = NULL;
80458 pMetadata->data.bext.codingHistorySize = 0;
80459 }
80460 }
80461 }
80462 return bytesRead;
80463}
80464MA_PRIVATE ma_uint64 ma_dr_wav__read_list_label_or_note_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize, ma_dr_wav_metadata_type type)
80465{
80466 ma_uint8 cueIDBuffer[MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES];
80467 ma_uint64 totalBytesRead = 0;
80468 size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead);
80469 MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
80470 if (bytesJustRead == sizeof(cueIDBuffer)) {
80471 ma_uint32 sizeIncludingNullTerminator;
80472 pMetadata->type = type;
80473 pMetadata->data.labelOrNote.cuePointId = ma_dr_wav_bytes_to_u32(cueIDBuffer);
80474 sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES;
80475 if (sizeIncludingNullTerminator > 0) {
80476 pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1;
80477 pMetadata->data.labelOrNote.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);
80478 MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);
80479 ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead);
80480 } else {
80481 pMetadata->data.labelOrNote.stringLength = 0;
80482 pMetadata->data.labelOrNote.pString = NULL;
80483 }
80484 }
80485 return totalBytesRead;
80486}
80487MA_PRIVATE ma_uint64 ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize)
80488{
80489 ma_uint8 buffer[MA_DR_WAV_LIST_LABELLED_TEXT_BYTES];
80490 ma_uint64 totalBytesRead = 0;
80491 size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead);
80492 MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
80493 if (bytesJustRead == sizeof(buffer)) {
80494 ma_uint32 sizeIncludingNullTerminator;
80495 pMetadata->type = ma_dr_wav_metadata_type_list_labelled_cue_region;
80496 pMetadata->data.labelledCueRegion.cuePointId = ma_dr_wav_bytes_to_u32(buffer + 0);
80497 pMetadata->data.labelledCueRegion.sampleLength = ma_dr_wav_bytes_to_u32(buffer + 4);
80498 pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8];
80499 pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9];
80500 pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10];
80501 pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11];
80502 pMetadata->data.labelledCueRegion.country = ma_dr_wav_bytes_to_u16(buffer + 12);
80503 pMetadata->data.labelledCueRegion.language = ma_dr_wav_bytes_to_u16(buffer + 14);
80504 pMetadata->data.labelledCueRegion.dialect = ma_dr_wav_bytes_to_u16(buffer + 16);
80505 pMetadata->data.labelledCueRegion.codePage = ma_dr_wav_bytes_to_u16(buffer + 18);
80506 sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES;
80507 if (sizeIncludingNullTerminator > 0) {
80508 pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1;
80509 pMetadata->data.labelledCueRegion.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);
80510 MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);
80511 ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead);
80512 } else {
80513 pMetadata->data.labelledCueRegion.stringLength = 0;
80514 pMetadata->data.labelledCueRegion.pString = NULL;
80515 }
80516 }
80517 return totalBytesRead;
80518}
80519MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_info_text_chunk(ma_dr_wav__metadata_parser* pParser, ma_uint64 chunkSize, ma_dr_wav_metadata_type type)
80520{
80521 ma_uint64 bytesRead = 0;
80522 ma_uint32 stringSizeWithNullTerminator = (ma_uint32)chunkSize;
80523 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
80524 pParser->metadataCount += 1;
80525 ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1);
80526 } else {
80527 ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];
80528 pMetadata->type = type;
80529 if (stringSizeWithNullTerminator > 0) {
80530 pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1;
80531 pMetadata->data.infoText.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1);
80532 MA_DR_WAV_ASSERT(pMetadata->data.infoText.pString != NULL);
80533 bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL);
80534 if (bytesRead == chunkSize) {
80535 pParser->metadataCursor += 1;
80536 } else {
80537 }
80538 } else {
80539 pMetadata->data.infoText.stringLength = 0;
80540 pMetadata->data.infoText.pString = NULL;
80541 pParser->metadataCursor += 1;
80542 }
80543 }
80544 return bytesRead;
80545}
80546MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_unknown_chunk(ma_dr_wav__metadata_parser* pParser, const ma_uint8* pChunkId, ma_uint64 chunkSize, ma_dr_wav_metadata_location location)
80547{
80548 ma_uint64 bytesRead = 0;
80549 if (location == ma_dr_wav_metadata_location_invalid) {
80550 return 0;
80551 }
80552 if (ma_dr_wav_fourcc_equal(pChunkId, "data") || ma_dr_wav_fourcc_equal(pChunkId, "fmt ") || ma_dr_wav_fourcc_equal(pChunkId, "fact")) {
80553 return 0;
80554 }
80555 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
80556 pParser->metadataCount += 1;
80557 ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1);
80558 } else {
80559 ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];
80560 pMetadata->type = ma_dr_wav_metadata_type_unknown;
80561 pMetadata->data.unknown.chunkLocation = location;
80562 pMetadata->data.unknown.id[0] = pChunkId[0];
80563 pMetadata->data.unknown.id[1] = pChunkId[1];
80564 pMetadata->data.unknown.id[2] = pChunkId[2];
80565 pMetadata->data.unknown.id[3] = pChunkId[3];
80566 pMetadata->data.unknown.dataSizeInBytes = (ma_uint32)chunkSize;
80567 pMetadata->data.unknown.pData = (ma_uint8 *)ma_dr_wav__metadata_get_memory(pParser, (size_t)chunkSize, 1);
80568 MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL);
80569 bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL);
80570 if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) {
80571 pParser->metadataCursor += 1;
80572 } else {
80573 }
80574 }
80575 return bytesRead;
80576}
80577MA_PRIVATE ma_bool32 ma_dr_wav__chunk_matches(ma_dr_wav_metadata_type allowedMetadataTypes, const ma_uint8* pChunkID, ma_dr_wav_metadata_type type, const char* pID)
80578{
80579 return (allowedMetadataTypes & type) && ma_dr_wav_fourcc_equal(pChunkID, pID);
80580}
80581MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata_type allowedMetadataTypes)
80582{
80583 const ma_uint8 *pChunkID = pChunkHeader->id.fourcc;
80584 ma_uint64 bytesRead = 0;
80585 if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_smpl, "smpl")) {
80586 if (pChunkHeader->sizeInBytes >= MA_DR_WAV_SMPL_BYTES) {
80587 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
80588 ma_uint8 buffer[4];
80589 size_t bytesJustRead;
80590 if (!pParser->onSeek(pParser->pReadSeekUserData, 28, MA_DR_WAV_SEEK_CUR)) {
80591 return bytesRead;
80592 }
80593 bytesRead += 28;
80594 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);
80595 if (bytesJustRead == sizeof(buffer)) {
80596 ma_uint32 loopCount = ma_dr_wav_bytes_to_u32(buffer);
80597 ma_uint64 calculatedLoopCount;
80598 calculatedLoopCount = (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES;
80599 if (calculatedLoopCount == loopCount) {
80600 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);
80601 if (bytesJustRead == sizeof(buffer)) {
80602 ma_uint32 samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(buffer);
80603 pParser->metadataCount += 1;
80604 ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_smpl_loop) * loopCount, MA_DR_WAV_METADATA_ALIGNMENT);
80605 ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1);
80606 }
80607 } else {
80608 }
80609 }
80610 } else {
80611 bytesRead = ma_dr_wav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);
80612 if (bytesRead == pChunkHeader->sizeInBytes) {
80613 pParser->metadataCursor += 1;
80614 } else {
80615 }
80616 }
80617 } else {
80618 }
80619 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_inst, "inst")) {
80620 if (pChunkHeader->sizeInBytes == MA_DR_WAV_INST_BYTES) {
80621 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
80622 pParser->metadataCount += 1;
80623 } else {
80624 bytesRead = ma_dr_wav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
80625 if (bytesRead == pChunkHeader->sizeInBytes) {
80626 pParser->metadataCursor += 1;
80627 } else {
80628 }
80629 }
80630 } else {
80631 }
80632 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_acid, "acid")) {
80633 if (pChunkHeader->sizeInBytes == MA_DR_WAV_ACID_BYTES) {
80634 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
80635 pParser->metadataCount += 1;
80636 } else {
80637 bytesRead = ma_dr_wav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
80638 if (bytesRead == pChunkHeader->sizeInBytes) {
80639 pParser->metadataCursor += 1;
80640 } else {
80641 }
80642 }
80643 } else {
80644 }
80645 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_cue, "cue ")) {
80646 if (pChunkHeader->sizeInBytes >= MA_DR_WAV_CUE_BYTES) {
80647 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
80648 size_t cueCount;
80649 pParser->metadataCount += 1;
80650 cueCount = (size_t)(pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES;
80651 ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_cue_point) * cueCount, MA_DR_WAV_METADATA_ALIGNMENT);
80652 } else {
80653 bytesRead = ma_dr_wav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);
80654 if (bytesRead == pChunkHeader->sizeInBytes) {
80655 pParser->metadataCursor += 1;
80656 } else {
80657 }
80658 }
80659 } else {
80660 }
80661 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_bext, "bext")) {
80662 if (pChunkHeader->sizeInBytes >= MA_DR_WAV_BEXT_BYTES) {
80663 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
80664 char buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES + 1];
80665 size_t allocSizeNeeded = MA_DR_WAV_BEXT_UMID_BYTES;
80666 size_t bytesJustRead;
80667 buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES] = '\0';
80668 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_DESCRIPTION_BYTES, &bytesRead);
80669 if (bytesJustRead != MA_DR_WAV_BEXT_DESCRIPTION_BYTES) {
80670 return bytesRead;
80671 }
80672 allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1;
80673 buffer[MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0';
80674 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead);
80675 if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES) {
80676 return bytesRead;
80677 }
80678 allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1;
80679 buffer[MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES] = '\0';
80680 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead);
80681 if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES) {
80682 return bytesRead;
80683 }
80684 allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1;
80685 allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES + 1;
80686 ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1);
80687 pParser->metadataCount += 1;
80688 } else {
80689 bytesRead = ma_dr_wav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes);
80690 if (bytesRead == pChunkHeader->sizeInBytes) {
80691 pParser->metadataCursor += 1;
80692 } else {
80693 }
80694 }
80695 } else {
80696 }
80697 } else if (ma_dr_wav_fourcc_equal(pChunkID, "LIST") || ma_dr_wav_fourcc_equal(pChunkID, "list")) {
80698 ma_dr_wav_metadata_location listType = ma_dr_wav_metadata_location_invalid;
80699 while (bytesRead < pChunkHeader->sizeInBytes) {
80700 ma_uint8 subchunkId[4];
80701 ma_uint8 subchunkSizeBuffer[4];
80702 ma_uint64 subchunkDataSize;
80703 ma_uint64 subchunkBytesRead = 0;
80704 ma_uint64 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead);
80705 if (bytesJustRead != sizeof(subchunkId)) {
80706 break;
80707 }
80708 if (ma_dr_wav_fourcc_equal(subchunkId, "adtl")) {
80709 listType = ma_dr_wav_metadata_location_inside_adtl_list;
80710 continue;
80711 } else if (ma_dr_wav_fourcc_equal(subchunkId, "INFO")) {
80712 listType = ma_dr_wav_metadata_location_inside_info_list;
80713 continue;
80714 }
80715 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead);
80716 if (bytesJustRead != sizeof(subchunkSizeBuffer)) {
80717 break;
80718 }
80719 subchunkDataSize = ma_dr_wav_bytes_to_u32(subchunkSizeBuffer);
80720 if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_label, "labl") || ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_note, "note")) {
80721 if (subchunkDataSize >= MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES) {
80722 ma_uint64 stringSizeWithNullTerm = subchunkDataSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES;
80723 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
80724 pParser->metadataCount += 1;
80725 ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1);
80726 } else {
80727 subchunkBytesRead = ma_dr_wav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, ma_dr_wav_fourcc_equal(subchunkId, "labl") ? ma_dr_wav_metadata_type_list_label : ma_dr_wav_metadata_type_list_note);
80728 if (subchunkBytesRead == subchunkDataSize) {
80729 pParser->metadataCursor += 1;
80730 } else {
80731 }
80732 }
80733 } else {
80734 }
80735 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_labelled_cue_region, "ltxt")) {
80736 if (subchunkDataSize >= MA_DR_WAV_LIST_LABELLED_TEXT_BYTES) {
80737 ma_uint64 stringSizeWithNullTerminator = subchunkDataSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES;
80738 if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
80739 pParser->metadataCount += 1;
80740 ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1);
80741 } else {
80742 subchunkBytesRead = ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize);
80743 if (subchunkBytesRead == subchunkDataSize) {
80744 pParser->metadataCursor += 1;
80745 } else {
80746 }
80747 }
80748 } else {
80749 }
80750 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_software, "ISFT")) {
80751 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_software);
80752 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_copyright, "ICOP")) {
80753 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_copyright);
80754 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_title, "INAM")) {
80755 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_title);
80756 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_artist, "IART")) {
80757 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_artist);
80758 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_comment, "ICMT")) {
80759 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_comment);
80760 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_date, "ICRD")) {
80761 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_date);
80762 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_genre, "IGNR")) {
80763 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_genre);
80764 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_album, "IPRD")) {
80765 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_album);
80766 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_tracknumber, "ITRK")) {
80767 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_tracknumber);
80768 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_location, "IARL")) {
80769 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_location);
80770 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_organization, "ICMS")) {
80771 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_organization);
80772 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_keywords, "IKEY")) {
80773 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_keywords);
80774 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_medium, "IMED")) {
80775 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_medium);
80776 } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_description, "ISBJ")) {
80777 subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_description);
80778 } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) {
80779 subchunkBytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType);
80780 }
80781 bytesRead += subchunkBytesRead;
80782 MA_DR_WAV_ASSERT(subchunkBytesRead <= subchunkDataSize);
80783 if (subchunkBytesRead < subchunkDataSize) {
80784 ma_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead;
80785 if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, MA_DR_WAV_SEEK_CUR)) {
80786 break;
80787 }
80788 bytesRead += bytesToSeek;
80789 }
80790 if ((subchunkDataSize % 2) == 1) {
80791 if (!pParser->onSeek(pParser->pReadSeekUserData, 1, MA_DR_WAV_SEEK_CUR)) {
80792 break;
80793 }
80794 bytesRead += 1;
80795 }
80796 }
80797 } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) {
80798 bytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, ma_dr_wav_metadata_location_top_level);
80799 }
80800 return bytesRead;
80801}
80802MA_PRIVATE ma_uint32 ma_dr_wav_get_bytes_per_pcm_frame(ma_dr_wav* pWav)
80803{
80804 ma_uint32 bytesPerFrame;
80805 if ((pWav->bitsPerSample & 0x7) == 0) {
80806 bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3;
80807 } else {
80808 bytesPerFrame = pWav->fmt.blockAlign;
80809 }
80810 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) {
80811 if (bytesPerFrame != pWav->fmt.channels) {
80812 return 0;
80813 }
80814 }
80815 return bytesPerFrame;
80816}
80817MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT)
80818{
80819 if (pFMT == NULL) {
80820 return 0;
80821 }
80822 if (pFMT->formatTag != MA_DR_WAVE_FORMAT_EXTENSIBLE) {
80823 return pFMT->formatTag;
80824 } else {
80825 return ma_dr_wav_bytes_to_u16(pFMT->subFormat);
80826 }
80827}
80828MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pReadSeekTellUserData, const ma_allocation_callbacks* pAllocationCallbacks)
80829{
80830 if (pWav == NULL || onRead == NULL || onSeek == NULL) {
80831 return MA_FALSE;
80832 }
80833 MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav));
80834 pWav->onRead = onRead;
80835 pWav->onSeek = onSeek;
80836 pWav->onTell = onTell;
80837 pWav->pUserData = pReadSeekTellUserData;
80838 pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
80839 if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
80840 return MA_FALSE;
80841 }
80842 return MA_TRUE;
80843}
80844MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags)
80845{
80846 ma_result result;
80847 ma_uint64 cursor;
80848 ma_bool32 sequential;
80849 ma_uint8 riff[4];
80850 ma_dr_wav_fmt fmt;
80851 unsigned short translatedFormatTag;
80852 ma_uint64 dataChunkSize = 0;
80853 ma_uint64 sampleCountFromFactChunk = 0;
80854 ma_uint64 metadataStartPos;
80855 ma_dr_wav__metadata_parser metadataParser;
80856 ma_bool8 isProcessingMetadata = MA_FALSE;
80857 ma_bool8 foundChunk_fmt = MA_FALSE;
80858 ma_bool8 foundChunk_data = MA_FALSE;
80859 ma_bool8 isAIFCFormType = MA_FALSE;
80860 ma_uint64 aiffFrameCount = 0;
80861 cursor = 0;
80862 sequential = (flags & MA_DR_WAV_SEQUENTIAL) != 0;
80863 MA_DR_WAV_ZERO_OBJECT(&fmt);
80864 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) {
80865 return MA_FALSE;
80866 }
80867 if (ma_dr_wav_fourcc_equal(riff, "RIFF")) {
80868 pWav->container = ma_dr_wav_container_riff;
80869 } else if (ma_dr_wav_fourcc_equal(riff, "RIFX")) {
80870 pWav->container = ma_dr_wav_container_rifx;
80871 } else if (ma_dr_wav_fourcc_equal(riff, "riff")) {
80872 int i;
80873 ma_uint8 riff2[12];
80874 pWav->container = ma_dr_wav_container_w64;
80875 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) {
80876 return MA_FALSE;
80877 }
80878 for (i = 0; i < 12; ++i) {
80879 if (riff2[i] != ma_dr_wavGUID_W64_RIFF[i+4]) {
80880 return MA_FALSE;
80881 }
80882 }
80883 } else if (ma_dr_wav_fourcc_equal(riff, "RF64")) {
80884 pWav->container = ma_dr_wav_container_rf64;
80885 } else if (ma_dr_wav_fourcc_equal(riff, "FORM")) {
80886 pWav->container = ma_dr_wav_container_aiff;
80887 } else {
80888 return MA_FALSE;
80889 }
80890 if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) {
80891 ma_uint8 chunkSizeBytes[4];
80892 ma_uint8 wave[4];
80893 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
80894 return MA_FALSE;
80895 }
80896 if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) {
80897 if (ma_dr_wav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) {
80898 }
80899 } else if (pWav->container == ma_dr_wav_container_rf64) {
80900 if (ma_dr_wav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) {
80901 return MA_FALSE;
80902 }
80903 } else {
80904 return MA_FALSE;
80905 }
80906 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
80907 return MA_FALSE;
80908 }
80909 if (!ma_dr_wav_fourcc_equal(wave, "WAVE")) {
80910 return MA_FALSE;
80911 }
80912 } else if (pWav->container == ma_dr_wav_container_w64) {
80913 ma_uint8 chunkSizeBytes[8];
80914 ma_uint8 wave[16];
80915 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
80916 return MA_FALSE;
80917 }
80918 if (ma_dr_wav_bytes_to_u64(chunkSizeBytes) < 80) {
80919 return MA_FALSE;
80920 }
80921 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
80922 return MA_FALSE;
80923 }
80924 if (!ma_dr_wav_guid_equal(wave, ma_dr_wavGUID_W64_WAVE)) {
80925 return MA_FALSE;
80926 }
80927 } else if (pWav->container == ma_dr_wav_container_aiff) {
80928 ma_uint8 chunkSizeBytes[4];
80929 ma_uint8 aiff[4];
80930 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
80931 return MA_FALSE;
80932 }
80933 if (ma_dr_wav_bytes_to_u32_be(chunkSizeBytes) < 18) {
80934 return MA_FALSE;
80935 }
80936 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) {
80937 return MA_FALSE;
80938 }
80939 if (ma_dr_wav_fourcc_equal(aiff, "AIFF")) {
80940 isAIFCFormType = MA_FALSE;
80941 } else if (ma_dr_wav_fourcc_equal(aiff, "AIFC")) {
80942 isAIFCFormType = MA_TRUE;
80943 } else {
80944 return MA_FALSE;
80945 }
80946 } else {
80947 return MA_FALSE;
80948 }
80949 if (pWav->container == ma_dr_wav_container_rf64) {
80950 ma_uint8 sizeBytes[8];
80951 ma_uint64 bytesRemainingInChunk;
80952 ma_dr_wav_chunk_header header;
80953 result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
80954 if (result != MA_SUCCESS) {
80955 return MA_FALSE;
80956 }
80957 if (!ma_dr_wav_fourcc_equal(header.id.fourcc, "ds64")) {
80958 return MA_FALSE;
80959 }
80960 bytesRemainingInChunk = header.sizeInBytes + header.paddingSize;
80961 if (!ma_dr_wav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) {
80962 return MA_FALSE;
80963 }
80964 bytesRemainingInChunk -= 8;
80965 cursor += 8;
80966 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
80967 return MA_FALSE;
80968 }
80969 bytesRemainingInChunk -= 8;
80970 dataChunkSize = ma_dr_wav_bytes_to_u64(sizeBytes);
80971 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
80972 return MA_FALSE;
80973 }
80974 bytesRemainingInChunk -= 8;
80975 sampleCountFromFactChunk = ma_dr_wav_bytes_to_u64(sizeBytes);
80976 if (!ma_dr_wav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) {
80977 return MA_FALSE;
80978 }
80979 cursor += bytesRemainingInChunk;
80980 }
80981 metadataStartPos = cursor;
80982 isProcessingMetadata = !sequential && ((flags & MA_DR_WAV_WITH_METADATA) != 0);
80983 if (pWav->container != ma_dr_wav_container_riff && pWav->container != ma_dr_wav_container_rf64) {
80984 isProcessingMetadata = MA_FALSE;
80985 }
80986 MA_DR_WAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser));
80987 if (isProcessingMetadata) {
80988 metadataParser.onRead = pWav->onRead;
80989 metadataParser.onSeek = pWav->onSeek;
80990 metadataParser.pReadSeekUserData = pWav->pUserData;
80991 metadataParser.stage = ma_dr_wav__metadata_parser_stage_count;
80992 }
80993 for (;;) {
80994 ma_dr_wav_chunk_header header;
80995 ma_uint64 chunkSize;
80996 result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
80997 if (result != MA_SUCCESS) {
80998 break;
80999 }
81000 chunkSize = header.sizeInBytes;
81001 if (!sequential && onChunk != NULL) {
81002 ma_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt);
81003 if (callbackBytesRead > 0) {
81004 if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) {
81005 return MA_FALSE;
81006 }
81007 }
81008 }
81009 if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fmt ")) ||
81010 ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FMT))) {
81011 ma_uint8 fmtData[16];
81012 foundChunk_fmt = MA_TRUE;
81013 if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) {
81014 return MA_FALSE;
81015 }
81016 cursor += sizeof(fmtData);
81017 fmt.formatTag = ma_dr_wav_bytes_to_u16_ex(fmtData + 0, pWav->container);
81018 fmt.channels = ma_dr_wav_bytes_to_u16_ex(fmtData + 2, pWav->container);
81019 fmt.sampleRate = ma_dr_wav_bytes_to_u32_ex(fmtData + 4, pWav->container);
81020 fmt.avgBytesPerSec = ma_dr_wav_bytes_to_u32_ex(fmtData + 8, pWav->container);
81021 fmt.blockAlign = ma_dr_wav_bytes_to_u16_ex(fmtData + 12, pWav->container);
81022 fmt.bitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtData + 14, pWav->container);
81023 fmt.extendedSize = 0;
81024 fmt.validBitsPerSample = 0;
81025 fmt.channelMask = 0;
81026 MA_DR_WAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat));
81027 if (header.sizeInBytes > 16) {
81028 ma_uint8 fmt_cbSize[2];
81029 int bytesReadSoFar = 0;
81030 if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) {
81031 return MA_FALSE;
81032 }
81033 cursor += sizeof(fmt_cbSize);
81034 bytesReadSoFar = 18;
81035 fmt.extendedSize = ma_dr_wav_bytes_to_u16_ex(fmt_cbSize, pWav->container);
81036 if (fmt.extendedSize > 0) {
81037 if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) {
81038 if (fmt.extendedSize != 22) {
81039 return MA_FALSE;
81040 }
81041 }
81042 if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) {
81043 ma_uint8 fmtext[22];
81044 if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) {
81045 return MA_FALSE;
81046 }
81047 fmt.validBitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtext + 0, pWav->container);
81048 fmt.channelMask = ma_dr_wav_bytes_to_u32_ex(fmtext + 2, pWav->container);
81049 ma_dr_wav_bytes_to_guid(fmtext + 6, fmt.subFormat);
81050 } else {
81051 if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, MA_DR_WAV_SEEK_CUR) == MA_FALSE) {
81052 return MA_FALSE;
81053 }
81054 }
81055 cursor += fmt.extendedSize;
81056 bytesReadSoFar += fmt.extendedSize;
81057 }
81058 if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), MA_DR_WAV_SEEK_CUR) == MA_FALSE) {
81059 return MA_FALSE;
81060 }
81061 cursor += (header.sizeInBytes - bytesReadSoFar);
81062 }
81063 if (header.paddingSize > 0) {
81064 if (ma_dr_wav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == MA_FALSE) {
81065 break;
81066 }
81067 cursor += header.paddingSize;
81068 }
81069 continue;
81070 }
81071 if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "data")) ||
81072 ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_DATA))) {
81073 foundChunk_data = MA_TRUE;
81074 pWav->dataChunkDataPos = cursor;
81075 if (pWav->container != ma_dr_wav_container_rf64) {
81076 dataChunkSize = chunkSize;
81077 }
81078 if (sequential || !isProcessingMetadata) {
81079 break;
81080 } else {
81081 chunkSize += header.paddingSize;
81082 if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) {
81083 break;
81084 }
81085 cursor += chunkSize;
81086 continue;
81087 }
81088 }
81089 if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fact")) ||
81090 ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FACT))) {
81091 if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) {
81092 ma_uint8 sampleCount[4];
81093 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) {
81094 return MA_FALSE;
81095 }
81096 chunkSize -= 4;
81097 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
81098 sampleCountFromFactChunk = ma_dr_wav_bytes_to_u32_ex(sampleCount, pWav->container);
81099 } else {
81100 sampleCountFromFactChunk = 0;
81101 }
81102 } else if (pWav->container == ma_dr_wav_container_w64) {
81103 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) {
81104 return MA_FALSE;
81105 }
81106 chunkSize -= 8;
81107 } else if (pWav->container == ma_dr_wav_container_rf64) {
81108 }
81109 chunkSize += header.paddingSize;
81110 if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) {
81111 break;
81112 }
81113 cursor += chunkSize;
81114 continue;
81115 }
81116 if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "COMM")) {
81117 ma_uint8 commData[24];
81118 ma_uint32 commDataBytesToRead;
81119 ma_uint16 channels;
81120 ma_uint32 frameCount;
81121 ma_uint16 sampleSizeInBits;
81122 ma_int64 sampleRate;
81123 ma_uint16 compressionFormat;
81124 foundChunk_fmt = MA_TRUE;
81125 if (isAIFCFormType) {
81126 commDataBytesToRead = 24;
81127 if (header.sizeInBytes < commDataBytesToRead) {
81128 return MA_FALSE;
81129 }
81130 } else {
81131 commDataBytesToRead = 18;
81132 if (header.sizeInBytes != commDataBytesToRead) {
81133 return MA_FALSE;
81134 }
81135 }
81136 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) {
81137 return MA_FALSE;
81138 }
81139 channels = ma_dr_wav_bytes_to_u16_ex (commData + 0, pWav->container);
81140 frameCount = ma_dr_wav_bytes_to_u32_ex (commData + 2, pWav->container);
81141 sampleSizeInBits = ma_dr_wav_bytes_to_u16_ex (commData + 6, pWav->container);
81142 sampleRate = ma_dr_wav_aiff_extented_to_s64(commData + 8);
81143 if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) {
81144 return MA_FALSE;
81145 }
81146 if (isAIFCFormType) {
81147 const ma_uint8* type = commData + 18;
81148 if (ma_dr_wav_fourcc_equal(type, "NONE")) {
81149 compressionFormat = MA_DR_WAVE_FORMAT_PCM;
81150 } else if (ma_dr_wav_fourcc_equal(type, "raw ")) {
81151 compressionFormat = MA_DR_WAVE_FORMAT_PCM;
81152 if (sampleSizeInBits == 8) {
81153 pWav->aiff.isUnsigned = MA_TRUE;
81154 }
81155 } else if (ma_dr_wav_fourcc_equal(type, "sowt")) {
81156 compressionFormat = MA_DR_WAVE_FORMAT_PCM;
81157 pWav->aiff.isLE = MA_TRUE;
81158 } else if (ma_dr_wav_fourcc_equal(type, "fl32") || ma_dr_wav_fourcc_equal(type, "fl64") || ma_dr_wav_fourcc_equal(type, "FL32") || ma_dr_wav_fourcc_equal(type, "FL64")) {
81159 compressionFormat = MA_DR_WAVE_FORMAT_IEEE_FLOAT;
81160 } else if (ma_dr_wav_fourcc_equal(type, "alaw") || ma_dr_wav_fourcc_equal(type, "ALAW")) {
81161 compressionFormat = MA_DR_WAVE_FORMAT_ALAW;
81162 } else if (ma_dr_wav_fourcc_equal(type, "ulaw") || ma_dr_wav_fourcc_equal(type, "ULAW")) {
81163 compressionFormat = MA_DR_WAVE_FORMAT_MULAW;
81164 } else if (ma_dr_wav_fourcc_equal(type, "ima4")) {
81165 compressionFormat = MA_DR_WAVE_FORMAT_DVI_ADPCM;
81166 sampleSizeInBits = 4;
81167 (void)compressionFormat;
81168 (void)sampleSizeInBits;
81169 return MA_FALSE;
81170 } else {
81171 return MA_FALSE;
81172 }
81173 } else {
81174 compressionFormat = MA_DR_WAVE_FORMAT_PCM;
81175 }
81176 aiffFrameCount = frameCount;
81177 fmt.formatTag = compressionFormat;
81178 fmt.channels = channels;
81179 fmt.sampleRate = (ma_uint32)sampleRate;
81180 fmt.bitsPerSample = sampleSizeInBits;
81181 fmt.blockAlign = (ma_uint16)(fmt.channels * fmt.bitsPerSample / 8);
81182 fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate;
81183 if (fmt.blockAlign == 0 && compressionFormat == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
81184 fmt.blockAlign = 34 * fmt.channels;
81185 }
81186 if (compressionFormat == MA_DR_WAVE_FORMAT_ALAW || compressionFormat == MA_DR_WAVE_FORMAT_MULAW) {
81187 if (fmt.bitsPerSample > 8) {
81188 fmt.bitsPerSample = 8;
81189 fmt.blockAlign = fmt.channels;
81190 }
81191 }
81192 fmt.bitsPerSample += (fmt.bitsPerSample & 7);
81193 if (isAIFCFormType) {
81194 if (ma_dr_wav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == MA_FALSE) {
81195 return MA_FALSE;
81196 }
81197 cursor += (chunkSize - commDataBytesToRead);
81198 }
81199 continue;
81200 }
81201 if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "SSND")) {
81202 ma_uint8 offsetAndBlockSizeData[8];
81203 ma_uint32 offset;
81204 foundChunk_data = MA_TRUE;
81205 if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) {
81206 return MA_FALSE;
81207 }
81208 offset = ma_dr_wav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container);
81209 pWav->dataChunkDataPos = cursor + offset;
81210 dataChunkSize = chunkSize;
81211 if (dataChunkSize > offset) {
81212 dataChunkSize -= offset;
81213 } else {
81214 dataChunkSize = 0;
81215 }
81216 if (sequential) {
81217 if (foundChunk_fmt) {
81218 if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) {
81219 return MA_FALSE;
81220 }
81221 cursor += offset;
81222 break;
81223 } else {
81224 return MA_FALSE;
81225 }
81226 } else {
81227 chunkSize += header.paddingSize;
81228 chunkSize -= sizeof(offsetAndBlockSizeData);
81229 if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) {
81230 break;
81231 }
81232 cursor += chunkSize;
81233 continue;
81234 }
81235 }
81236 if (isProcessingMetadata) {
81237 ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown);
81238 if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) {
81239 break;
81240 }
81241 }
81242 chunkSize += header.paddingSize;
81243 if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) {
81244 break;
81245 }
81246 cursor += chunkSize;
81247 }
81248 if (!foundChunk_fmt || !foundChunk_data) {
81249 return MA_FALSE;
81250 }
81251 if ((fmt.sampleRate == 0 || fmt.sampleRate > MA_DR_WAV_MAX_SAMPLE_RATE ) ||
81252 (fmt.channels == 0 || fmt.channels > MA_DR_WAV_MAX_CHANNELS ) ||
81253 (fmt.bitsPerSample == 0 || fmt.bitsPerSample > MA_DR_WAV_MAX_BITS_PER_SAMPLE) ||
81254 fmt.blockAlign == 0) {
81255 return MA_FALSE;
81256 }
81257 translatedFormatTag = fmt.formatTag;
81258 if (translatedFormatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) {
81259 translatedFormatTag = ma_dr_wav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container);
81260 }
81261 if (!sequential) {
81262 if (!ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) {
81263 return MA_FALSE;
81264 }
81265 cursor = pWav->dataChunkDataPos;
81266 }
81267 if (isProcessingMetadata && metadataParser.metadataCount > 0) {
81268 if (ma_dr_wav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == MA_FALSE) {
81269 return MA_FALSE;
81270 }
81271 result = ma_dr_wav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks);
81272 if (result != MA_SUCCESS) {
81273 return MA_FALSE;
81274 }
81275 metadataParser.stage = ma_dr_wav__metadata_parser_stage_read;
81276 for (;;) {
81277 ma_dr_wav_chunk_header header;
81278 ma_uint64 metadataBytesRead;
81279 result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
81280 if (result != MA_SUCCESS) {
81281 break;
81282 }
81283 metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown);
81284 if (ma_dr_wav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == MA_FALSE) {
81285 ma_dr_wav_free(metadataParser.pMetadata, &pWav->allocationCallbacks);
81286 return MA_FALSE;
81287 }
81288 }
81289 pWav->pMetadata = metadataParser.pMetadata;
81290 pWav->metadataCount = metadataParser.metadataCount;
81291 }
81292 if (pWav->onTell != NULL && pWav->onSeek != NULL) {
81293 if (pWav->onSeek(pWav->pUserData, 0, MA_DR_WAV_SEEK_END) == MA_TRUE) {
81294 ma_int64 fileSize;
81295 if (pWav->onTell(pWav->pUserData, &fileSize)) {
81296 if (dataChunkSize + pWav->dataChunkDataPos > (ma_uint64)fileSize) {
81297 dataChunkSize = (ma_uint64)fileSize - pWav->dataChunkDataPos;
81298 }
81299 }
81300 } else {
81301 }
81302 }
81303 if (dataChunkSize == 0xFFFFFFFF && (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) && pWav->isSequentialWrite == MA_FALSE) {
81304 dataChunkSize = 0;
81305 for (;;) {
81306 ma_uint8 temp[4096];
81307 size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp));
81308 dataChunkSize += bytesRead;
81309 if (bytesRead < sizeof(temp)) {
81310 break;
81311 }
81312 }
81313 }
81314 if (ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == MA_FALSE) {
81315 ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);
81316 return MA_FALSE;
81317 }
81318 pWav->fmt = fmt;
81319 pWav->sampleRate = fmt.sampleRate;
81320 pWav->channels = fmt.channels;
81321 pWav->bitsPerSample = fmt.bitsPerSample;
81322 pWav->translatedFormatTag = translatedFormatTag;
81323 if (!ma_dr_wav__is_compressed_format_tag(translatedFormatTag)) {
81324 ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
81325 if (bytesPerFrame > 0) {
81326 dataChunkSize -= (dataChunkSize % bytesPerFrame);
81327 }
81328 }
81329 pWav->bytesRemaining = dataChunkSize;
81330 pWav->dataChunkDataSize = dataChunkSize;
81331 if (sampleCountFromFactChunk != 0) {
81332 pWav->totalPCMFrameCount = sampleCountFromFactChunk;
81333 } else if (aiffFrameCount != 0) {
81334 pWav->totalPCMFrameCount = aiffFrameCount;
81335 } else {
81336 ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
81337 if (bytesPerFrame == 0) {
81338 ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);
81339 return MA_FALSE;
81340 }
81341 pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame;
81342 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
81343 ma_uint64 totalBlockHeaderSizeInBytes;
81344 ma_uint64 blockCount = dataChunkSize / fmt.blockAlign;
81345 if ((blockCount * fmt.blockAlign) < dataChunkSize) {
81346 blockCount += 1;
81347 }
81348 totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels);
81349 pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
81350 }
81351 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
81352 ma_uint64 totalBlockHeaderSizeInBytes;
81353 ma_uint64 blockCount = dataChunkSize / fmt.blockAlign;
81354 if ((blockCount * fmt.blockAlign) < dataChunkSize) {
81355 blockCount += 1;
81356 }
81357 totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels);
81358 pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
81359 pWav->totalPCMFrameCount += blockCount;
81360 }
81361 }
81362 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
81363 if (pWav->channels > 2) {
81364 ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);
81365 return MA_FALSE;
81366 }
81367 }
81368 if (ma_dr_wav_get_bytes_per_pcm_frame(pWav) == 0) {
81369 ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);
81370 return MA_FALSE;
81371 }
81372#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
81373 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
81374 ma_uint64 blockCount = dataChunkSize / fmt.blockAlign;
81375 pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels;
81376 }
81377 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
81378 ma_uint64 blockCount = dataChunkSize / fmt.blockAlign;
81379 pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels;
81380 }
81381#endif
81382 return MA_TRUE;
81383}
81384MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
81385{
81386 return ma_dr_wav_init_ex(pWav, onRead, onSeek, onTell, NULL, pUserData, NULL, 0, pAllocationCallbacks);
81387}
81388MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, ma_dr_wav_chunk_proc onChunk, void* pReadSeekTellUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
81389{
81390 if (!ma_dr_wav_preinit(pWav, onRead, onSeek, onTell, pReadSeekTellUserData, pAllocationCallbacks)) {
81391 return MA_FALSE;
81392 }
81393 return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags);
81394}
81395MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
81396{
81397 if (!ma_dr_wav_preinit(pWav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
81398 return MA_FALSE;
81399 }
81400 return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA);
81401}
81402MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav)
81403{
81404 ma_dr_wav_metadata *result = pWav->pMetadata;
81405 pWav->pMetadata = NULL;
81406 pWav->metadataCount = 0;
81407 return result;
81408}
81409MA_PRIVATE size_t ma_dr_wav__write(ma_dr_wav* pWav, const void* pData, size_t dataSize)
81410{
81411 MA_DR_WAV_ASSERT(pWav != NULL);
81412 MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
81413 return pWav->onWrite(pWav->pUserData, pData, dataSize);
81414}
81415MA_PRIVATE size_t ma_dr_wav__write_byte(ma_dr_wav* pWav, ma_uint8 byte)
81416{
81417 MA_DR_WAV_ASSERT(pWav != NULL);
81418 MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
81419 return pWav->onWrite(pWav->pUserData, &byte, 1);
81420}
81421MA_PRIVATE size_t ma_dr_wav__write_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value)
81422{
81423 MA_DR_WAV_ASSERT(pWav != NULL);
81424 MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
81425 if (!ma_dr_wav__is_little_endian()) {
81426 value = ma_dr_wav__bswap16(value);
81427 }
81428 return ma_dr_wav__write(pWav, &value, 2);
81429}
81430MA_PRIVATE size_t ma_dr_wav__write_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value)
81431{
81432 MA_DR_WAV_ASSERT(pWav != NULL);
81433 MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
81434 if (!ma_dr_wav__is_little_endian()) {
81435 value = ma_dr_wav__bswap32(value);
81436 }
81437 return ma_dr_wav__write(pWav, &value, 4);
81438}
81439MA_PRIVATE size_t ma_dr_wav__write_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value)
81440{
81441 MA_DR_WAV_ASSERT(pWav != NULL);
81442 MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
81443 if (!ma_dr_wav__is_little_endian()) {
81444 value = ma_dr_wav__bswap64(value);
81445 }
81446 return ma_dr_wav__write(pWav, &value, 8);
81447}
81448MA_PRIVATE size_t ma_dr_wav__write_f32ne_to_le(ma_dr_wav* pWav, float value)
81449{
81450 union {
81451 ma_uint32 u32;
81452 float f32;
81453 } u;
81454 MA_DR_WAV_ASSERT(pWav != NULL);
81455 MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
81456 u.f32 = value;
81457 if (!ma_dr_wav__is_little_endian()) {
81458 u.u32 = ma_dr_wav__bswap32(u.u32);
81459 }
81460 return ma_dr_wav__write(pWav, &u.u32, 4);
81461}
81462MA_PRIVATE size_t ma_dr_wav__write_or_count(ma_dr_wav* pWav, const void* pData, size_t dataSize)
81463{
81464 if (pWav == NULL) {
81465 return dataSize;
81466 }
81467 return ma_dr_wav__write(pWav, pData, dataSize);
81468}
81469MA_PRIVATE size_t ma_dr_wav__write_or_count_byte(ma_dr_wav* pWav, ma_uint8 byte)
81470{
81471 if (pWav == NULL) {
81472 return 1;
81473 }
81474 return ma_dr_wav__write_byte(pWav, byte);
81475}
81476MA_PRIVATE size_t ma_dr_wav__write_or_count_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value)
81477{
81478 if (pWav == NULL) {
81479 return 2;
81480 }
81481 return ma_dr_wav__write_u16ne_to_le(pWav, value);
81482}
81483MA_PRIVATE size_t ma_dr_wav__write_or_count_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value)
81484{
81485 if (pWav == NULL) {
81486 return 4;
81487 }
81488 return ma_dr_wav__write_u32ne_to_le(pWav, value);
81489}
81490#if 0
81491MA_PRIVATE size_t ma_dr_wav__write_or_count_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value)
81492{
81493 if (pWav == NULL) {
81494 return 8;
81495 }
81496 return ma_dr_wav__write_u64ne_to_le(pWav, value);
81497}
81498#endif
81499MA_PRIVATE size_t ma_dr_wav__write_or_count_f32ne_to_le(ma_dr_wav* pWav, float value)
81500{
81501 if (pWav == NULL) {
81502 return 4;
81503 }
81504 return ma_dr_wav__write_f32ne_to_le(pWav, value);
81505}
81506MA_PRIVATE size_t ma_dr_wav__write_or_count_string_to_fixed_size_buf(ma_dr_wav* pWav, char* str, size_t bufFixedSize)
81507{
81508 size_t len;
81509 if (pWav == NULL) {
81510 return bufFixedSize;
81511 }
81512 len = ma_dr_wav__strlen_clamped(str, bufFixedSize);
81513 ma_dr_wav__write_or_count(pWav, str, len);
81514 if (len < bufFixedSize) {
81515 size_t i;
81516 for (i = 0; i < bufFixedSize - len; ++i) {
81517 ma_dr_wav__write_byte(pWav, 0);
81518 }
81519 }
81520 return bufFixedSize;
81521}
81522MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_metadata* pMetadatas, ma_uint32 metadataCount)
81523{
81524 size_t bytesWritten = 0;
81525 ma_bool32 hasListAdtl = MA_FALSE;
81526 ma_bool32 hasListInfo = MA_FALSE;
81527 ma_uint32 iMetadata;
81528 if (pMetadatas == NULL || metadataCount == 0) {
81529 return 0;
81530 }
81531 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
81532 ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];
81533 ma_uint32 chunkSize = 0;
81534 if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list)) {
81535 hasListInfo = MA_TRUE;
81536 }
81537 if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_adtl) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list)) {
81538 hasListAdtl = MA_TRUE;
81539 }
81540 switch (pMetadata->type) {
81541 case ma_dr_wav_metadata_type_smpl:
81542 {
81543 ma_uint32 iLoop;
81544 chunkSize = MA_DR_WAV_SMPL_BYTES + MA_DR_WAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes;
81545 bytesWritten += ma_dr_wav__write_or_count(pWav, "smpl", 4);
81546 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
81547 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId);
81548 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId);
81549 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds);
81550 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote);
81551 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction);
81552 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat);
81553 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset);
81554 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount);
81555 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);
81556 for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) {
81557 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId);
81558 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type);
81559 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleOffset);
81560 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleOffset);
81561 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction);
81562 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount);
81563 }
81564 if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {
81565 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);
81566 }
81567 } break;
81568 case ma_dr_wav_metadata_type_inst:
81569 {
81570 chunkSize = MA_DR_WAV_INST_BYTES;
81571 bytesWritten += ma_dr_wav__write_or_count(pWav, "inst", 4);
81572 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
81573 bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1);
81574 bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1);
81575 bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1);
81576 bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1);
81577 bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1);
81578 bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1);
81579 bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1);
81580 } break;
81581 case ma_dr_wav_metadata_type_cue:
81582 {
81583 ma_uint32 iCuePoint;
81584 chunkSize = MA_DR_WAV_CUE_BYTES + MA_DR_WAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount;
81585 bytesWritten += ma_dr_wav__write_or_count(pWav, "cue ", 4);
81586 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
81587 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount);
81588 for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {
81589 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id);
81590 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition);
81591 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4);
81592 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart);
81593 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart);
81594 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset);
81595 }
81596 } break;
81597 case ma_dr_wav_metadata_type_acid:
81598 {
81599 chunkSize = MA_DR_WAV_ACID_BYTES;
81600 bytesWritten += ma_dr_wav__write_or_count(pWav, "acid", 4);
81601 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
81602 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags);
81603 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote);
81604 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1);
81605 bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2);
81606 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats);
81607 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator);
81608 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator);
81609 bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo);
81610 } break;
81611 case ma_dr_wav_metadata_type_bext:
81612 {
81613 char reservedBuf[MA_DR_WAV_BEXT_RESERVED_BYTES];
81614 ma_uint32 timeReferenceLow;
81615 ma_uint32 timeReferenceHigh;
81616 chunkSize = MA_DR_WAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize;
81617 bytesWritten += ma_dr_wav__write_or_count(pWav, "bext", 4);
81618 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
81619 bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, MA_DR_WAV_BEXT_DESCRIPTION_BYTES);
81620 bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES);
81621 bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES);
81622 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate));
81623 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime));
81624 timeReferenceLow = (ma_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF);
81625 timeReferenceHigh = (ma_uint32)(pMetadata->data.bext.timeReference >> 32);
81626 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceLow);
81627 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh);
81628 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version);
81629 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES);
81630 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue);
81631 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange);
81632 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel);
81633 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness);
81634 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness);
81635 MA_DR_WAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf));
81636 bytesWritten += ma_dr_wav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf));
81637 if (pMetadata->data.bext.codingHistorySize > 0) {
81638 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize);
81639 }
81640 } break;
81641 case ma_dr_wav_metadata_type_unknown:
81642 {
81643 if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_top_level) {
81644 chunkSize = pMetadata->data.unknown.dataSizeInBytes;
81645 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
81646 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
81647 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes);
81648 }
81649 } break;
81650 default: break;
81651 }
81652 if ((chunkSize % 2) != 0) {
81653 bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0);
81654 }
81655 }
81656 if (hasListInfo) {
81657 ma_uint32 chunkSize = 4;
81658 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
81659 ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];
81660 if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings)) {
81661 chunkSize += 8;
81662 chunkSize += pMetadata->data.infoText.stringLength + 1;
81663 } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) {
81664 chunkSize += 8;
81665 chunkSize += pMetadata->data.unknown.dataSizeInBytes;
81666 }
81667 if ((chunkSize % 2) != 0) {
81668 chunkSize += 1;
81669 }
81670 }
81671 bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4);
81672 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
81673 bytesWritten += ma_dr_wav__write_or_count(pWav, "INFO", 4);
81674 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
81675 ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];
81676 ma_uint32 subchunkSize = 0;
81677 if (pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) {
81678 const char* pID = NULL;
81679 switch (pMetadata->type) {
81680 case ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break;
81681 case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break;
81682 case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break;
81683 case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break;
81684 case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break;
81685 case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break;
81686 case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break;
81687 case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break;
81688 case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break;
81689 case ma_dr_wav_metadata_type_list_info_location: pID = "IARL"; break;
81690 case ma_dr_wav_metadata_type_list_info_organization: pID = "ICMS"; break;
81691 case ma_dr_wav_metadata_type_list_info_keywords: pID = "IKEY"; break;
81692 case ma_dr_wav_metadata_type_list_info_medium: pID = "IMED"; break;
81693 case ma_dr_wav_metadata_type_list_info_description: pID = "ISBJ"; break;
81694 default: break;
81695 }
81696 MA_DR_WAV_ASSERT(pID != NULL);
81697 if (pMetadata->data.infoText.stringLength) {
81698 subchunkSize = pMetadata->data.infoText.stringLength + 1;
81699 bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4);
81700 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize);
81701 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength);
81702 bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0');
81703 }
81704 } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) {
81705 if (pMetadata->data.unknown.dataSizeInBytes) {
81706 subchunkSize = pMetadata->data.unknown.dataSizeInBytes;
81707 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
81708 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes);
81709 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);
81710 }
81711 }
81712 if ((subchunkSize % 2) != 0) {
81713 bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0);
81714 }
81715 }
81716 }
81717 if (hasListAdtl) {
81718 ma_uint32 chunkSize = 4;
81719 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
81720 ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];
81721 switch (pMetadata->type)
81722 {
81723 case ma_dr_wav_metadata_type_list_label:
81724 case ma_dr_wav_metadata_type_list_note:
81725 {
81726 chunkSize += 8;
81727 chunkSize += MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES;
81728 if (pMetadata->data.labelOrNote.stringLength > 0) {
81729 chunkSize += pMetadata->data.labelOrNote.stringLength + 1;
81730 }
81731 } break;
81732 case ma_dr_wav_metadata_type_list_labelled_cue_region:
81733 {
81734 chunkSize += 8;
81735 chunkSize += MA_DR_WAV_LIST_LABELLED_TEXT_BYTES;
81736 if (pMetadata->data.labelledCueRegion.stringLength > 0) {
81737 chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;
81738 }
81739 } break;
81740 case ma_dr_wav_metadata_type_unknown:
81741 {
81742 if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) {
81743 chunkSize += 8;
81744 chunkSize += pMetadata->data.unknown.dataSizeInBytes;
81745 }
81746 } break;
81747 default: break;
81748 }
81749 if ((chunkSize % 2) != 0) {
81750 chunkSize += 1;
81751 }
81752 }
81753 bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4);
81754 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
81755 bytesWritten += ma_dr_wav__write_or_count(pWav, "adtl", 4);
81756 for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
81757 ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];
81758 ma_uint32 subchunkSize = 0;
81759 switch (pMetadata->type)
81760 {
81761 case ma_dr_wav_metadata_type_list_label:
81762 case ma_dr_wav_metadata_type_list_note:
81763 {
81764 if (pMetadata->data.labelOrNote.stringLength > 0) {
81765 const char *pID = NULL;
81766 if (pMetadata->type == ma_dr_wav_metadata_type_list_label) {
81767 pID = "labl";
81768 }
81769 else if (pMetadata->type == ma_dr_wav_metadata_type_list_note) {
81770 pID = "note";
81771 }
81772 MA_DR_WAV_ASSERT(pID != NULL);
81773 MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);
81774 subchunkSize = MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES;
81775 bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4);
81776 subchunkSize += pMetadata->data.labelOrNote.stringLength + 1;
81777 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize);
81778 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId);
81779 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength);
81780 bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0');
81781 }
81782 } break;
81783 case ma_dr_wav_metadata_type_list_labelled_cue_region:
81784 {
81785 subchunkSize = MA_DR_WAV_LIST_LABELLED_TEXT_BYTES;
81786 bytesWritten += ma_dr_wav__write_or_count(pWav, "ltxt", 4);
81787 if (pMetadata->data.labelledCueRegion.stringLength > 0) {
81788 subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;
81789 }
81790 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize);
81791 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId);
81792 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength);
81793 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4);
81794 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country);
81795 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language);
81796 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect);
81797 bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage);
81798 if (pMetadata->data.labelledCueRegion.stringLength > 0) {
81799 MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);
81800 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength);
81801 bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0');
81802 }
81803 } break;
81804 case ma_dr_wav_metadata_type_unknown:
81805 {
81806 if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) {
81807 subchunkSize = pMetadata->data.unknown.dataSizeInBytes;
81808 MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL);
81809 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
81810 bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize);
81811 bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);
81812 }
81813 } break;
81814 default: break;
81815 }
81816 if ((subchunkSize % 2) != 0) {
81817 bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0);
81818 }
81819 }
81820 }
81821 MA_DR_WAV_ASSERT((bytesWritten % 2) == 0);
81822 return bytesWritten;
81823}
81824MA_PRIVATE ma_uint32 ma_dr_wav__riff_chunk_size_riff(ma_uint64 dataChunkSize, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount)
81825{
81826 ma_uint64 chunkSize = 4 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize);
81827 if (chunkSize > 0xFFFFFFFFUL) {
81828 chunkSize = 0xFFFFFFFFUL;
81829 }
81830 return (ma_uint32)chunkSize;
81831}
81832MA_PRIVATE ma_uint32 ma_dr_wav__data_chunk_size_riff(ma_uint64 dataChunkSize)
81833{
81834 if (dataChunkSize <= 0xFFFFFFFFUL) {
81835 return (ma_uint32)dataChunkSize;
81836 } else {
81837 return 0xFFFFFFFFUL;
81838 }
81839}
81840MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_w64(ma_uint64 dataChunkSize)
81841{
81842 ma_uint64 dataSubchunkPaddingSize = ma_dr_wav__chunk_padding_size_w64(dataChunkSize);
81843 return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize;
81844}
81845MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_w64(ma_uint64 dataChunkSize)
81846{
81847 return 24 + dataChunkSize;
81848}
81849MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_rf64(ma_uint64 dataChunkSize, ma_dr_wav_metadata *metadata, ma_uint32 numMetadata)
81850{
81851 ma_uint64 chunkSize = 4 + 36 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize);
81852 if (chunkSize > 0xFFFFFFFFUL) {
81853 chunkSize = 0xFFFFFFFFUL;
81854 }
81855 return chunkSize;
81856}
81857MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_rf64(ma_uint64 dataChunkSize)
81858{
81859 return dataChunkSize;
81860}
81861MA_PRIVATE ma_bool32 ma_dr_wav_preinit_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_bool32 isSequential, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
81862{
81863 if (pWav == NULL || onWrite == NULL) {
81864 return MA_FALSE;
81865 }
81866 if (!isSequential && onSeek == NULL) {
81867 return MA_FALSE;
81868 }
81869 if (pFormat->format == MA_DR_WAVE_FORMAT_EXTENSIBLE) {
81870 return MA_FALSE;
81871 }
81872 if (pFormat->format == MA_DR_WAVE_FORMAT_ADPCM || pFormat->format == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
81873 return MA_FALSE;
81874 }
81875 MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav));
81876 pWav->onWrite = onWrite;
81877 pWav->onSeek = onSeek;
81878 pWav->pUserData = pUserData;
81879 pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
81880 if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
81881 return MA_FALSE;
81882 }
81883 pWav->fmt.formatTag = (ma_uint16)pFormat->format;
81884 pWav->fmt.channels = (ma_uint16)pFormat->channels;
81885 pWav->fmt.sampleRate = pFormat->sampleRate;
81886 pWav->fmt.avgBytesPerSec = (ma_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8);
81887 pWav->fmt.blockAlign = (ma_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8);
81888 pWav->fmt.bitsPerSample = (ma_uint16)pFormat->bitsPerSample;
81889 pWav->fmt.extendedSize = 0;
81890 pWav->isSequentialWrite = isSequential;
81891 return MA_TRUE;
81892}
81893MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount)
81894{
81895 size_t runningPos = 0;
81896 ma_uint64 initialDataChunkSize = 0;
81897 ma_uint64 chunkSizeFMT;
81898 if (pWav->isSequentialWrite) {
81899 initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8;
81900 if (pFormat->container == ma_dr_wav_container_riff) {
81901 if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) {
81902 return MA_FALSE;
81903 }
81904 }
81905 }
81906 pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize;
81907 if (pFormat->container == ma_dr_wav_container_riff) {
81908 ma_uint32 chunkSizeRIFF = 36 + (ma_uint32)initialDataChunkSize;
81909 runningPos += ma_dr_wav__write(pWav, "RIFF", 4);
81910 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeRIFF);
81911 runningPos += ma_dr_wav__write(pWav, "WAVE", 4);
81912 } else if (pFormat->container == ma_dr_wav_container_w64) {
81913 ma_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize;
81914 runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_RIFF, 16);
81915 runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeRIFF);
81916 runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_WAVE, 16);
81917 } else if (pFormat->container == ma_dr_wav_container_rf64) {
81918 runningPos += ma_dr_wav__write(pWav, "RF64", 4);
81919 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF);
81920 runningPos += ma_dr_wav__write(pWav, "WAVE", 4);
81921 } else {
81922 return MA_FALSE;
81923 }
81924 if (pFormat->container == ma_dr_wav_container_rf64) {
81925 ma_uint32 initialds64ChunkSize = 28;
81926 ma_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize;
81927 runningPos += ma_dr_wav__write(pWav, "ds64", 4);
81928 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, initialds64ChunkSize);
81929 runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialRiffChunkSize);
81930 runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialDataChunkSize);
81931 runningPos += ma_dr_wav__write_u64ne_to_le(pWav, totalSampleCount);
81932 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0);
81933 }
81934 if (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64) {
81935 chunkSizeFMT = 16;
81936 runningPos += ma_dr_wav__write(pWav, "fmt ", 4);
81937 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, (ma_uint32)chunkSizeFMT);
81938 } else if (pFormat->container == ma_dr_wav_container_w64) {
81939 chunkSizeFMT = 40;
81940 runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_FMT, 16);
81941 runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeFMT);
81942 }
81943 runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.formatTag);
81944 runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.channels);
81945 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate);
81946 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec);
81947 runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign);
81948 runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample);
81949 if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64)) {
81950 runningPos += ma_dr_wav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount);
81951 }
81952 pWav->dataChunkDataPos = runningPos;
81953 if (pFormat->container == ma_dr_wav_container_riff) {
81954 ma_uint32 chunkSizeDATA = (ma_uint32)initialDataChunkSize;
81955 runningPos += ma_dr_wav__write(pWav, "data", 4);
81956 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeDATA);
81957 } else if (pFormat->container == ma_dr_wav_container_w64) {
81958 ma_uint64 chunkSizeDATA = 24 + initialDataChunkSize;
81959 runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_DATA, 16);
81960 runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeDATA);
81961 } else if (pFormat->container == ma_dr_wav_container_rf64) {
81962 runningPos += ma_dr_wav__write(pWav, "data", 4);
81963 runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF);
81964 }
81965 pWav->container = pFormat->container;
81966 pWav->channels = (ma_uint16)pFormat->channels;
81967 pWav->sampleRate = pFormat->sampleRate;
81968 pWav->bitsPerSample = (ma_uint16)pFormat->bitsPerSample;
81969 pWav->translatedFormatTag = (ma_uint16)pFormat->format;
81970 pWav->dataChunkDataPos = runningPos;
81971 return MA_TRUE;
81972}
81973MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
81974{
81975 if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
81976 return MA_FALSE;
81977 }
81978 return ma_dr_wav_init_write__internal(pWav, pFormat, 0);
81979}
81980MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
81981{
81982 if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) {
81983 return MA_FALSE;
81984 }
81985 return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount);
81986}
81987MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
81988{
81989 if (pFormat == NULL) {
81990 return MA_FALSE;
81991 }
81992 return ma_dr_wav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks);
81993}
81994MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount)
81995{
81996 if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
81997 return MA_FALSE;
81998 }
81999 pWav->pMetadata = pMetadata;
82000 pWav->metadataCount = metadataCount;
82001 return ma_dr_wav_init_write__internal(pWav, pFormat, 0);
82002}
82003MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount)
82004{
82005 ma_uint64 targetDataSizeBytes = (ma_uint64)((ma_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0);
82006 ma_uint64 riffChunkSizeBytes;
82007 ma_uint64 fileSizeBytes = 0;
82008 if (pFormat->container == ma_dr_wav_container_riff) {
82009 riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount);
82010 fileSizeBytes = (8 + riffChunkSizeBytes);
82011 } else if (pFormat->container == ma_dr_wav_container_w64) {
82012 riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_w64(targetDataSizeBytes);
82013 fileSizeBytes = riffChunkSizeBytes;
82014 } else if (pFormat->container == ma_dr_wav_container_rf64) {
82015 riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount);
82016 fileSizeBytes = (8 + riffChunkSizeBytes);
82017 }
82018 return fileSizeBytes;
82019}
82020#ifndef MA_DR_WAV_NO_STDIO
82021MA_PRIVATE size_t ma_dr_wav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
82022{
82023 return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
82024}
82025MA_PRIVATE size_t ma_dr_wav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite)
82026{
82027 return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData);
82028}
82029MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_stdio(void* pUserData, int offset, ma_dr_wav_seek_origin origin)
82030{
82031 int whence = SEEK_SET;
82032 if (origin == MA_DR_WAV_SEEK_CUR) {
82033 whence = SEEK_CUR;
82034 } else if (origin == MA_DR_WAV_SEEK_END) {
82035 whence = SEEK_END;
82036 }
82037 return fseek((FILE*)pUserData, offset, whence) == 0;
82038}
82039MA_PRIVATE ma_bool32 ma_dr_wav__on_tell_stdio(void* pUserData, ma_int64* pCursor)
82040{
82041 FILE* pFileStdio = (FILE*)pUserData;
82042 ma_int64 result;
82043 MA_DR_WAV_ASSERT(pFileStdio != NULL);
82044 MA_DR_WAV_ASSERT(pCursor != NULL);
82045#if defined(_WIN32)
82046 #if defined(_MSC_VER) && _MSC_VER > 1200
82047 result = _ftelli64(pFileStdio);
82048 #else
82049 result = ftell(pFileStdio);
82050 #endif
82051#else
82052 result = ftell(pFileStdio);
82053#endif
82054 *pCursor = result;
82055 return MA_TRUE;
82056}
82057MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks)
82058{
82059 return ma_dr_wav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
82060}
82061MA_PRIVATE ma_bool32 ma_dr_wav_init_file__internal_FILE(ma_dr_wav* pWav, FILE* pFile, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
82062{
82063 ma_bool32 result;
82064 result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, ma_dr_wav__on_tell_stdio, (void*)pFile, pAllocationCallbacks);
82065 if (result != MA_TRUE) {
82066 fclose(pFile);
82067 return result;
82068 }
82069 result = ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags);
82070 if (result != MA_TRUE) {
82071 fclose(pFile);
82072 return result;
82073 }
82074 return MA_TRUE;
82075}
82076MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
82077{
82078 FILE* pFile;
82079 if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) {
82080 return MA_FALSE;
82081 }
82082 return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);
82083}
82084#ifndef MA_DR_WAV_NO_WCHAR
82085MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks)
82086{
82087 return ma_dr_wav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
82088}
82089MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
82090{
82091 FILE* pFile;
82092 if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) {
82093 return MA_FALSE;
82094 }
82095 return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);
82096}
82097#endif
82098MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
82099{
82100 FILE* pFile;
82101 if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) {
82102 return MA_FALSE;
82103 }
82104 return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks);
82105}
82106#ifndef MA_DR_WAV_NO_WCHAR
82107MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
82108{
82109 FILE* pFile;
82110 if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) {
82111 return MA_FALSE;
82112 }
82113 return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks);
82114}
82115#endif
82116MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal_FILE(ma_dr_wav* pWav, FILE* pFile, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks)
82117{
82118 ma_bool32 result;
82119 result = ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
82120 if (result != MA_TRUE) {
82121 fclose(pFile);
82122 return result;
82123 }
82124 result = ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount);
82125 if (result != MA_TRUE) {
82126 fclose(pFile);
82127 return result;
82128 }
82129 return MA_TRUE;
82130}
82131MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks)
82132{
82133 FILE* pFile;
82134 if (ma_fopen(&pFile, filename, "wb") != MA_SUCCESS) {
82135 return MA_FALSE;
82136 }
82137 return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
82138}
82139#ifndef MA_DR_WAV_NO_WCHAR
82140MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write_w__internal(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks)
82141{
82142 FILE* pFile;
82143 if (ma_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != MA_SUCCESS) {
82144 return MA_FALSE;
82145 }
82146 return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
82147}
82148#endif
82149MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks)
82150{
82151 return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks);
82152}
82153MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks)
82154{
82155 return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks);
82156}
82157MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
82158{
82159 if (pFormat == NULL) {
82160 return MA_FALSE;
82161 }
82162 return ma_dr_wav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
82163}
82164#ifndef MA_DR_WAV_NO_WCHAR
82165MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks)
82166{
82167 return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks);
82168}
82169MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks)
82170{
82171 return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks);
82172}
82173MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
82174{
82175 if (pFormat == NULL) {
82176 return MA_FALSE;
82177 }
82178 return ma_dr_wav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
82179}
82180#endif
82181#endif
82182MA_PRIVATE size_t ma_dr_wav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
82183{
82184 ma_dr_wav* pWav = (ma_dr_wav*)pUserData;
82185 size_t bytesRemaining;
82186 MA_DR_WAV_ASSERT(pWav != NULL);
82187 MA_DR_WAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos);
82188 bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos;
82189 if (bytesToRead > bytesRemaining) {
82190 bytesToRead = bytesRemaining;
82191 }
82192 if (bytesToRead > 0) {
82193 MA_DR_WAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead);
82194 pWav->memoryStream.currentReadPos += bytesToRead;
82195 }
82196 return bytesToRead;
82197}
82198MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory(void* pUserData, int offset, ma_dr_wav_seek_origin origin)
82199{
82200 ma_dr_wav* pWav = (ma_dr_wav*)pUserData;
82201 ma_int64 newCursor;
82202 MA_DR_WAV_ASSERT(pWav != NULL);
82203 newCursor = pWav->memoryStream.currentReadPos;
82204 if (origin == MA_DR_WAV_SEEK_SET) {
82205 newCursor = 0;
82206 } else if (origin == MA_DR_WAV_SEEK_CUR) {
82207 newCursor = (ma_int64)pWav->memoryStream.currentReadPos;
82208 } else if (origin == MA_DR_WAV_SEEK_END) {
82209 newCursor = (ma_int64)pWav->memoryStream.dataSize;
82210 } else {
82211 MA_DR_WAV_ASSERT(!"Invalid seek origin");
82212 return MA_FALSE;
82213 }
82214 newCursor += offset;
82215 if (newCursor < 0) {
82216 return MA_FALSE;
82217 }
82218 if ((size_t)newCursor > pWav->memoryStream.dataSize) {
82219 return MA_FALSE;
82220 }
82221 pWav->memoryStream.currentReadPos = (size_t)newCursor;
82222 return MA_TRUE;
82223}
82224MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)
82225{
82226 ma_dr_wav* pWav = (ma_dr_wav*)pUserData;
82227 size_t bytesRemaining;
82228 MA_DR_WAV_ASSERT(pWav != NULL);
82229 MA_DR_WAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos);
82230 bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos;
82231 if (bytesRemaining < bytesToWrite) {
82232 void* pNewData;
82233 size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2;
82234 if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) {
82235 newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite;
82236 }
82237 pNewData = ma_dr_wav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks);
82238 if (pNewData == NULL) {
82239 return 0;
82240 }
82241 *pWav->memoryStreamWrite.ppData = pNewData;
82242 pWav->memoryStreamWrite.dataCapacity = newDataCapacity;
82243 }
82244 MA_DR_WAV_COPY_MEMORY(((ma_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite);
82245 pWav->memoryStreamWrite.currentWritePos += bytesToWrite;
82246 if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) {
82247 pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos;
82248 }
82249 *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize;
82250 return bytesToWrite;
82251}
82252MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset, ma_dr_wav_seek_origin origin)
82253{
82254 ma_dr_wav* pWav = (ma_dr_wav*)pUserData;
82255 ma_int64 newCursor;
82256 MA_DR_WAV_ASSERT(pWav != NULL);
82257 newCursor = pWav->memoryStreamWrite.currentWritePos;
82258 if (origin == MA_DR_WAV_SEEK_SET) {
82259 newCursor = 0;
82260 } else if (origin == MA_DR_WAV_SEEK_CUR) {
82261 newCursor = (ma_int64)pWav->memoryStreamWrite.currentWritePos;
82262 } else if (origin == MA_DR_WAV_SEEK_END) {
82263 newCursor = (ma_int64)pWav->memoryStreamWrite.dataSize;
82264 } else {
82265 MA_DR_WAV_ASSERT(!"Invalid seek origin");
82266 return MA_INVALID_ARGS;
82267 }
82268 newCursor += offset;
82269 if (newCursor < 0) {
82270 return MA_FALSE;
82271 }
82272 if ((size_t)newCursor > pWav->memoryStreamWrite.dataSize) {
82273 return MA_FALSE;
82274 }
82275 pWav->memoryStreamWrite.currentWritePos = (size_t)newCursor;
82276 return MA_TRUE;
82277}
82278MA_PRIVATE ma_bool32 ma_dr_wav__on_tell_memory(void* pUserData, ma_int64* pCursor)
82279{
82280 ma_dr_wav* pWav = (ma_dr_wav*)pUserData;
82281 MA_DR_WAV_ASSERT(pWav != NULL);
82282 MA_DR_WAV_ASSERT(pCursor != NULL);
82283 *pCursor = (ma_int64)pWav->memoryStream.currentReadPos;
82284 return MA_TRUE;
82285}
82286MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks)
82287{
82288 return ma_dr_wav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks);
82289}
82290MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
82291{
82292 if (data == NULL || dataSize == 0) {
82293 return MA_FALSE;
82294 }
82295 if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, ma_dr_wav__on_tell_memory, pWav, pAllocationCallbacks)) {
82296 return MA_FALSE;
82297 }
82298 pWav->memoryStream.data = (const ma_uint8*)data;
82299 pWav->memoryStream.dataSize = dataSize;
82300 pWav->memoryStream.currentReadPos = 0;
82301 return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags);
82302}
82303MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
82304{
82305 if (data == NULL || dataSize == 0) {
82306 return MA_FALSE;
82307 }
82308 if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, ma_dr_wav__on_tell_memory, pWav, pAllocationCallbacks)) {
82309 return MA_FALSE;
82310 }
82311 pWav->memoryStream.data = (const ma_uint8*)data;
82312 pWav->memoryStream.dataSize = dataSize;
82313 pWav->memoryStream.currentReadPos = 0;
82314 return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA);
82315}
82316MA_PRIVATE ma_bool32 ma_dr_wav_init_memory_write__internal(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks)
82317{
82318 if (ppData == NULL || pDataSize == NULL) {
82319 return MA_FALSE;
82320 }
82321 *ppData = NULL;
82322 *pDataSize = 0;
82323 if (!ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_memory, ma_dr_wav__on_seek_memory_write, pWav, pAllocationCallbacks)) {
82324 return MA_FALSE;
82325 }
82326 pWav->memoryStreamWrite.ppData = ppData;
82327 pWav->memoryStreamWrite.pDataSize = pDataSize;
82328 pWav->memoryStreamWrite.dataSize = 0;
82329 pWav->memoryStreamWrite.dataCapacity = 0;
82330 pWav->memoryStreamWrite.currentWritePos = 0;
82331 return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount);
82332}
82333MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks)
82334{
82335 return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, MA_FALSE, pAllocationCallbacks);
82336}
82337MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks)
82338{
82339 return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks);
82340}
82341MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
82342{
82343 if (pFormat == NULL) {
82344 return MA_FALSE;
82345 }
82346 return ma_dr_wav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
82347}
82348MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav)
82349{
82350 ma_result result = MA_SUCCESS;
82351 if (pWav == NULL) {
82352 return MA_INVALID_ARGS;
82353 }
82354 if (pWav->onWrite != NULL) {
82355 ma_uint32 paddingSize = 0;
82356 if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rf64) {
82357 paddingSize = ma_dr_wav__chunk_padding_size_riff(pWav->dataChunkDataSize);
82358 } else {
82359 paddingSize = ma_dr_wav__chunk_padding_size_w64(pWav->dataChunkDataSize);
82360 }
82361 if (paddingSize > 0) {
82362 ma_uint64 paddingData = 0;
82363 ma_dr_wav__write(pWav, &paddingData, paddingSize);
82364 }
82365 if (pWav->onSeek && !pWav->isSequentialWrite) {
82366 if (pWav->container == ma_dr_wav_container_riff) {
82367 if (pWav->onSeek(pWav->pUserData, 4, MA_DR_WAV_SEEK_SET)) {
82368 ma_uint32 riffChunkSize = ma_dr_wav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
82369 ma_dr_wav__write_u32ne_to_le(pWav, riffChunkSize);
82370 }
82371 if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, MA_DR_WAV_SEEK_SET)) {
82372 ma_uint32 dataChunkSize = ma_dr_wav__data_chunk_size_riff(pWav->dataChunkDataSize);
82373 ma_dr_wav__write_u32ne_to_le(pWav, dataChunkSize);
82374 }
82375 } else if (pWav->container == ma_dr_wav_container_w64) {
82376 if (pWav->onSeek(pWav->pUserData, 16, MA_DR_WAV_SEEK_SET)) {
82377 ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_w64(pWav->dataChunkDataSize);
82378 ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize);
82379 }
82380 if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, MA_DR_WAV_SEEK_SET)) {
82381 ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_w64(pWav->dataChunkDataSize);
82382 ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize);
82383 }
82384 } else if (pWav->container == ma_dr_wav_container_rf64) {
82385 int ds64BodyPos = 12 + 8;
82386 if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, MA_DR_WAV_SEEK_SET)) {
82387 ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
82388 ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize);
82389 }
82390 if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, MA_DR_WAV_SEEK_SET)) {
82391 ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_rf64(pWav->dataChunkDataSize);
82392 ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize);
82393 }
82394 }
82395 }
82396 if (pWav->isSequentialWrite) {
82397 if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) {
82398 result = MA_INVALID_FILE;
82399 }
82400 }
82401 } else {
82402 ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);
82403 }
82404#ifndef MA_DR_WAV_NO_STDIO
82405 if (pWav->onRead == ma_dr_wav__on_read_stdio || pWav->onWrite == ma_dr_wav__on_write_stdio) {
82406 fclose((FILE*)pWav->pUserData);
82407 }
82408#endif
82409 return result;
82410}
82411MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut)
82412{
82413 size_t bytesRead;
82414 ma_uint32 bytesPerFrame;
82415 if (pWav == NULL || bytesToRead == 0) {
82416 return 0;
82417 }
82418 if (bytesToRead > pWav->bytesRemaining) {
82419 bytesToRead = (size_t)pWav->bytesRemaining;
82420 }
82421 if (bytesToRead == 0) {
82422 return 0;
82423 }
82424 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
82425 if (bytesPerFrame == 0) {
82426 return 0;
82427 }
82428 if (pBufferOut != NULL) {
82429 bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);
82430 } else {
82431 bytesRead = 0;
82432 while (bytesRead < bytesToRead) {
82433 size_t bytesToSeek = (bytesToRead - bytesRead);
82434 if (bytesToSeek > 0x7FFFFFFF) {
82435 bytesToSeek = 0x7FFFFFFF;
82436 }
82437 if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, MA_DR_WAV_SEEK_CUR) == MA_FALSE) {
82438 break;
82439 }
82440 bytesRead += bytesToSeek;
82441 }
82442 while (bytesRead < bytesToRead) {
82443 ma_uint8 buffer[4096];
82444 size_t bytesSeeked;
82445 size_t bytesToSeek = (bytesToRead - bytesRead);
82446 if (bytesToSeek > sizeof(buffer)) {
82447 bytesToSeek = sizeof(buffer);
82448 }
82449 bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek);
82450 bytesRead += bytesSeeked;
82451 if (bytesSeeked < bytesToSeek) {
82452 break;
82453 }
82454 }
82455 }
82456 pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame;
82457 pWav->bytesRemaining -= bytesRead;
82458 return bytesRead;
82459}
82460MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut)
82461{
82462 ma_uint32 bytesPerFrame;
82463 ma_uint64 bytesToRead;
82464 ma_uint64 framesRemainingInFile;
82465 if (pWav == NULL || framesToRead == 0) {
82466 return 0;
82467 }
82468 if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) {
82469 return 0;
82470 }
82471 framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames;
82472 if (framesToRead > framesRemainingInFile) {
82473 framesToRead = framesRemainingInFile;
82474 }
82475 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
82476 if (bytesPerFrame == 0) {
82477 return 0;
82478 }
82479 bytesToRead = framesToRead * bytesPerFrame;
82480 if (bytesToRead > MA_SIZE_MAX) {
82481 bytesToRead = (MA_SIZE_MAX / bytesPerFrame) * bytesPerFrame;
82482 }
82483 if (bytesToRead == 0) {
82484 return 0;
82485 }
82486 return ma_dr_wav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame;
82487}
82488MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut)
82489{
82490 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
82491 if (pBufferOut != NULL) {
82492 ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
82493 if (bytesPerFrame == 0) {
82494 return 0;
82495 }
82496 ma_dr_wav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels);
82497 }
82498 return framesRead;
82499}
82500MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut)
82501{
82502 ma_uint64 framesRead = 0;
82503 if (ma_dr_wav_is_container_be(pWav->container)) {
82504 if (pWav->container != ma_dr_wav_container_aiff || pWav->aiff.isLE == MA_FALSE) {
82505 if (ma_dr_wav__is_little_endian()) {
82506 framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);
82507 } else {
82508 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
82509 }
82510 goto post_process;
82511 }
82512 }
82513 if (ma_dr_wav__is_little_endian()) {
82514 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
82515 } else {
82516 framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);
82517 }
82518 post_process:
82519 {
82520 if (pWav->container == ma_dr_wav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == MA_FALSE) {
82521 if (pBufferOut != NULL) {
82522 ma_uint64 iSample;
82523 for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) {
82524 ((ma_uint8*)pBufferOut)[iSample] += 128;
82525 }
82526 }
82527 }
82528 }
82529 return framesRead;
82530}
82531MA_PRIVATE ma_bool32 ma_dr_wav_seek_to_first_pcm_frame(ma_dr_wav* pWav)
82532{
82533 if (pWav->onWrite != NULL) {
82534 return MA_FALSE;
82535 }
82536 if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, MA_DR_WAV_SEEK_SET)) {
82537 return MA_FALSE;
82538 }
82539 if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) {
82540 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
82541 MA_DR_WAV_ZERO_OBJECT(&pWav->msadpcm);
82542 } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
82543 MA_DR_WAV_ZERO_OBJECT(&pWav->ima);
82544 } else {
82545 MA_DR_WAV_ASSERT(MA_FALSE);
82546 }
82547 }
82548 pWav->readCursorInPCMFrames = 0;
82549 pWav->bytesRemaining = pWav->dataChunkDataSize;
82550 return MA_TRUE;
82551}
82552MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex)
82553{
82554 if (pWav == NULL || pWav->onSeek == NULL) {
82555 return MA_FALSE;
82556 }
82557 if (pWav->onWrite != NULL) {
82558 return MA_FALSE;
82559 }
82560 if (pWav->totalPCMFrameCount == 0) {
82561 return MA_TRUE;
82562 }
82563 if (targetFrameIndex > pWav->totalPCMFrameCount) {
82564 targetFrameIndex = pWav->totalPCMFrameCount;
82565 }
82566 if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) {
82567 if (targetFrameIndex < pWav->readCursorInPCMFrames) {
82568 if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) {
82569 return MA_FALSE;
82570 }
82571 }
82572 if (targetFrameIndex > pWav->readCursorInPCMFrames) {
82573 ma_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames;
82574 ma_int16 devnull[2048];
82575 while (offsetInFrames > 0) {
82576 ma_uint64 framesRead = 0;
82577 ma_uint64 framesToRead = offsetInFrames;
82578 if (framesToRead > ma_dr_wav_countof(devnull)/pWav->channels) {
82579 framesToRead = ma_dr_wav_countof(devnull)/pWav->channels;
82580 }
82581 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
82582 framesRead = ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull);
82583 } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
82584 framesRead = ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull);
82585 } else {
82586 MA_DR_WAV_ASSERT(MA_FALSE);
82587 }
82588 if (framesRead != framesToRead) {
82589 return MA_FALSE;
82590 }
82591 offsetInFrames -= framesRead;
82592 }
82593 }
82594 } else {
82595 ma_uint64 totalSizeInBytes;
82596 ma_uint64 currentBytePos;
82597 ma_uint64 targetBytePos;
82598 ma_uint64 offset;
82599 ma_uint32 bytesPerFrame;
82600 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
82601 if (bytesPerFrame == 0) {
82602 return MA_FALSE;
82603 }
82604 totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame;
82605 currentBytePos = totalSizeInBytes - pWav->bytesRemaining;
82606 targetBytePos = targetFrameIndex * bytesPerFrame;
82607 if (currentBytePos < targetBytePos) {
82608 offset = (targetBytePos - currentBytePos);
82609 } else {
82610 if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) {
82611 return MA_FALSE;
82612 }
82613 offset = targetBytePos;
82614 }
82615 while (offset > 0) {
82616 int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);
82617 if (!pWav->onSeek(pWav->pUserData, offset32, MA_DR_WAV_SEEK_CUR)) {
82618 return MA_FALSE;
82619 }
82620 pWav->readCursorInPCMFrames += offset32 / bytesPerFrame;
82621 pWav->bytesRemaining -= offset32;
82622 offset -= offset32;
82623 }
82624 }
82625 return MA_TRUE;
82626}
82627MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor)
82628{
82629 if (pCursor == NULL) {
82630 return MA_INVALID_ARGS;
82631 }
82632 *pCursor = 0;
82633 if (pWav == NULL) {
82634 return MA_INVALID_ARGS;
82635 }
82636 *pCursor = pWav->readCursorInPCMFrames;
82637 return MA_SUCCESS;
82638}
82639MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength)
82640{
82641 if (pLength == NULL) {
82642 return MA_INVALID_ARGS;
82643 }
82644 *pLength = 0;
82645 if (pWav == NULL) {
82646 return MA_INVALID_ARGS;
82647 }
82648 *pLength = pWav->totalPCMFrameCount;
82649 return MA_SUCCESS;
82650}
82651MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData)
82652{
82653 size_t bytesWritten;
82654 if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {
82655 return 0;
82656 }
82657 bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);
82658 pWav->dataChunkDataSize += bytesWritten;
82659 return bytesWritten;
82660}
82661MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData)
82662{
82663 ma_uint64 bytesToWrite;
82664 ma_uint64 bytesWritten;
82665 const ma_uint8* pRunningData;
82666 if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
82667 return 0;
82668 }
82669 bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
82670 if (bytesToWrite > MA_SIZE_MAX) {
82671 return 0;
82672 }
82673 bytesWritten = 0;
82674 pRunningData = (const ma_uint8*)pData;
82675 while (bytesToWrite > 0) {
82676 size_t bytesJustWritten;
82677 ma_uint64 bytesToWriteThisIteration;
82678 bytesToWriteThisIteration = bytesToWrite;
82679 MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX);
82680 bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData);
82681 if (bytesJustWritten == 0) {
82682 break;
82683 }
82684 bytesToWrite -= bytesJustWritten;
82685 bytesWritten += bytesJustWritten;
82686 pRunningData += bytesJustWritten;
82687 }
82688 return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
82689}
82690MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData)
82691{
82692 ma_uint64 bytesToWrite;
82693 ma_uint64 bytesWritten;
82694 ma_uint32 bytesPerSample;
82695 const ma_uint8* pRunningData;
82696 if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
82697 return 0;
82698 }
82699 bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
82700 if (bytesToWrite > MA_SIZE_MAX) {
82701 return 0;
82702 }
82703 bytesWritten = 0;
82704 pRunningData = (const ma_uint8*)pData;
82705 bytesPerSample = ma_dr_wav_get_bytes_per_pcm_frame(pWav) / pWav->channels;
82706 if (bytesPerSample == 0) {
82707 return 0;
82708 }
82709 while (bytesToWrite > 0) {
82710 ma_uint8 temp[4096];
82711 ma_uint32 sampleCount;
82712 size_t bytesJustWritten;
82713 ma_uint64 bytesToWriteThisIteration;
82714 bytesToWriteThisIteration = bytesToWrite;
82715 MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX);
82716 sampleCount = sizeof(temp)/bytesPerSample;
82717 if (bytesToWriteThisIteration > ((ma_uint64)sampleCount)*bytesPerSample) {
82718 bytesToWriteThisIteration = ((ma_uint64)sampleCount)*bytesPerSample;
82719 }
82720 MA_DR_WAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration);
82721 ma_dr_wav__bswap_samples(temp, sampleCount, bytesPerSample);
82722 bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp);
82723 if (bytesJustWritten == 0) {
82724 break;
82725 }
82726 bytesToWrite -= bytesJustWritten;
82727 bytesWritten += bytesJustWritten;
82728 pRunningData += bytesJustWritten;
82729 }
82730 return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
82731}
82732MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData)
82733{
82734 if (ma_dr_wav__is_little_endian()) {
82735 return ma_dr_wav_write_pcm_frames_le(pWav, framesToWrite, pData);
82736 } else {
82737 return ma_dr_wav_write_pcm_frames_be(pWav, framesToWrite, pData);
82738 }
82739}
82740MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
82741{
82742 ma_uint64 totalFramesRead = 0;
82743 static ma_int32 adaptationTable[] = {
82744 230, 230, 230, 230, 307, 409, 512, 614,
82745 768, 614, 512, 409, 307, 230, 230, 230
82746 };
82747 static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
82748 static ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
82749 MA_DR_WAV_ASSERT(pWav != NULL);
82750 MA_DR_WAV_ASSERT(framesToRead > 0);
82751 while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
82752 MA_DR_WAV_ASSERT(framesToRead > 0);
82753 if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) {
82754 if (pWav->channels == 1) {
82755 ma_uint8 header[7];
82756 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
82757 return totalFramesRead;
82758 }
82759 pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
82760 pWav->msadpcm.predictor[0] = header[0];
82761 pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 1);
82762 pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 3);
82763 pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 5);
82764 pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0];
82765 pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1];
82766 pWav->msadpcm.cachedFrameCount = 2;
82767 if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table)) {
82768 return totalFramesRead;
82769 }
82770 } else {
82771 ma_uint8 header[14];
82772 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
82773 return totalFramesRead;
82774 }
82775 pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
82776 pWav->msadpcm.predictor[0] = header[0];
82777 pWav->msadpcm.predictor[1] = header[1];
82778 pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 2);
82779 pWav->msadpcm.delta[1] = ma_dr_wav_bytes_to_s16(header + 4);
82780 pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 6);
82781 pWav->msadpcm.prevFrames[1][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 8);
82782 pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 10);
82783 pWav->msadpcm.prevFrames[1][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 12);
82784 pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0];
82785 pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0];
82786 pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1];
82787 pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1];
82788 pWav->msadpcm.cachedFrameCount = 2;
82789 if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= ma_dr_wav_countof(coeff2Table)) {
82790 return totalFramesRead;
82791 }
82792 }
82793 }
82794 while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
82795 if (pBufferOut != NULL) {
82796 ma_uint32 iSample = 0;
82797 for (iSample = 0; iSample < pWav->channels; iSample += 1) {
82798 pBufferOut[iSample] = (ma_int16)pWav->msadpcm.cachedFrames[(ma_dr_wav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample];
82799 }
82800 pBufferOut += pWav->channels;
82801 }
82802 framesToRead -= 1;
82803 totalFramesRead += 1;
82804 pWav->readCursorInPCMFrames += 1;
82805 pWav->msadpcm.cachedFrameCount -= 1;
82806 }
82807 if (framesToRead == 0) {
82808 break;
82809 }
82810 if (pWav->msadpcm.cachedFrameCount == 0) {
82811 if (pWav->msadpcm.bytesRemainingInBlock == 0) {
82812 continue;
82813 } else {
82814 ma_uint8 nibbles;
82815 ma_int32 nibble0;
82816 ma_int32 nibble1;
82817 if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {
82818 return totalFramesRead;
82819 }
82820 pWav->msadpcm.bytesRemainingInBlock -= 1;
82821 nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }
82822 nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }
82823 if (pWav->channels == 1) {
82824 ma_int32 newSample0;
82825 ma_int32 newSample1;
82826 newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
82827 newSample0 += nibble0 * pWav->msadpcm.delta[0];
82828 newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767);
82829 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
82830 if (pWav->msadpcm.delta[0] < 16) {
82831 pWav->msadpcm.delta[0] = 16;
82832 }
82833 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
82834 pWav->msadpcm.prevFrames[0][1] = newSample0;
82835 newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
82836 newSample1 += nibble1 * pWav->msadpcm.delta[0];
82837 newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767);
82838 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;
82839 if (pWav->msadpcm.delta[0] < 16) {
82840 pWav->msadpcm.delta[0] = 16;
82841 }
82842 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
82843 pWav->msadpcm.prevFrames[0][1] = newSample1;
82844 pWav->msadpcm.cachedFrames[2] = newSample0;
82845 pWav->msadpcm.cachedFrames[3] = newSample1;
82846 pWav->msadpcm.cachedFrameCount = 2;
82847 } else {
82848 ma_int32 newSample0;
82849 ma_int32 newSample1;
82850 newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
82851 newSample0 += nibble0 * pWav->msadpcm.delta[0];
82852 newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767);
82853 pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
82854 if (pWav->msadpcm.delta[0] < 16) {
82855 pWav->msadpcm.delta[0] = 16;
82856 }
82857 pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
82858 pWav->msadpcm.prevFrames[0][1] = newSample0;
82859 newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
82860 newSample1 += nibble1 * pWav->msadpcm.delta[1];
82861 newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767);
82862 pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;
82863 if (pWav->msadpcm.delta[1] < 16) {
82864 pWav->msadpcm.delta[1] = 16;
82865 }
82866 pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1];
82867 pWav->msadpcm.prevFrames[1][1] = newSample1;
82868 pWav->msadpcm.cachedFrames[2] = newSample0;
82869 pWav->msadpcm.cachedFrames[3] = newSample1;
82870 pWav->msadpcm.cachedFrameCount = 1;
82871 }
82872 }
82873 }
82874 }
82875 return totalFramesRead;
82876}
82877MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
82878{
82879 ma_uint64 totalFramesRead = 0;
82880 ma_uint32 iChannel;
82881 static ma_int32 indexTable[16] = {
82882 -1, -1, -1, -1, 2, 4, 6, 8,
82883 -1, -1, -1, -1, 2, 4, 6, 8
82884 };
82885 static ma_int32 stepTable[89] = {
82886 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
82887 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
82888 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
82889 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
82890 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
82891 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
82892 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
82893 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
82894 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
82895 };
82896 MA_DR_WAV_ASSERT(pWav != NULL);
82897 MA_DR_WAV_ASSERT(framesToRead > 0);
82898 while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
82899 MA_DR_WAV_ASSERT(framesToRead > 0);
82900 if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) {
82901 if (pWav->channels == 1) {
82902 ma_uint8 header[4];
82903 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
82904 return totalFramesRead;
82905 }
82906 pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
82907 if (header[2] >= ma_dr_wav_countof(stepTable)) {
82908 pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, MA_DR_WAV_SEEK_CUR);
82909 pWav->ima.bytesRemainingInBlock = 0;
82910 return totalFramesRead;
82911 }
82912 pWav->ima.predictor[0] = (ma_int16)ma_dr_wav_bytes_to_u16(header + 0);
82913 pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);
82914 pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0];
82915 pWav->ima.cachedFrameCount = 1;
82916 } else {
82917 ma_uint8 header[8];
82918 if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
82919 return totalFramesRead;
82920 }
82921 pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
82922 if (header[2] >= ma_dr_wav_countof(stepTable) || header[6] >= ma_dr_wav_countof(stepTable)) {
82923 pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, MA_DR_WAV_SEEK_CUR);
82924 pWav->ima.bytesRemainingInBlock = 0;
82925 return totalFramesRead;
82926 }
82927 pWav->ima.predictor[0] = ma_dr_wav_bytes_to_s16(header + 0);
82928 pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);
82929 pWav->ima.predictor[1] = ma_dr_wav_bytes_to_s16(header + 4);
82930 pWav->ima.stepIndex[1] = ma_dr_wav_clamp(header[6], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);
82931 pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0];
82932 pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1];
82933 pWav->ima.cachedFrameCount = 1;
82934 }
82935 }
82936 while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
82937 if (pBufferOut != NULL) {
82938 ma_uint32 iSample;
82939 for (iSample = 0; iSample < pWav->channels; iSample += 1) {
82940 pBufferOut[iSample] = (ma_int16)pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample];
82941 }
82942 pBufferOut += pWav->channels;
82943 }
82944 framesToRead -= 1;
82945 totalFramesRead += 1;
82946 pWav->readCursorInPCMFrames += 1;
82947 pWav->ima.cachedFrameCount -= 1;
82948 }
82949 if (framesToRead == 0) {
82950 break;
82951 }
82952 if (pWav->ima.cachedFrameCount == 0) {
82953 if (pWav->ima.bytesRemainingInBlock == 0) {
82954 continue;
82955 } else {
82956 pWav->ima.cachedFrameCount = 8;
82957 for (iChannel = 0; iChannel < pWav->channels; ++iChannel) {
82958 ma_uint32 iByte;
82959 ma_uint8 nibbles[4];
82960 if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) {
82961 pWav->ima.cachedFrameCount = 0;
82962 return totalFramesRead;
82963 }
82964 pWav->ima.bytesRemainingInBlock -= 4;
82965 for (iByte = 0; iByte < 4; ++iByte) {
82966 ma_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0);
82967 ma_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4);
82968 ma_int32 step = stepTable[pWav->ima.stepIndex[iChannel]];
82969 ma_int32 predictor = pWav->ima.predictor[iChannel];
82970 ma_int32 diff = step >> 3;
82971 if (nibble0 & 1) diff += step >> 2;
82972 if (nibble0 & 2) diff += step >> 1;
82973 if (nibble0 & 4) diff += step;
82974 if (nibble0 & 8) diff = -diff;
82975 predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767);
82976 pWav->ima.predictor[iChannel] = predictor;
82977 pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);
82978 pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor;
82979 step = stepTable[pWav->ima.stepIndex[iChannel]];
82980 predictor = pWav->ima.predictor[iChannel];
82981 diff = step >> 3;
82982 if (nibble1 & 1) diff += step >> 2;
82983 if (nibble1 & 2) diff += step >> 1;
82984 if (nibble1 & 4) diff += step;
82985 if (nibble1 & 8) diff = -diff;
82986 predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767);
82987 pWav->ima.predictor[iChannel] = predictor;
82988 pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);
82989 pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor;
82990 }
82991 }
82992 }
82993 }
82994 }
82995 return totalFramesRead;
82996}
82997#ifndef MA_DR_WAV_NO_CONVERSION_API
82998static unsigned short ma_dr_wav_gAlawTable[256] = {
82999 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
83000 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
83001 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
83002 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,
83003 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,
83004 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,
83005 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,
83006 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,
83007 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,
83008 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,
83009 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,
83010 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,
83011 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,
83012 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,
83013 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,
83014 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
83015};
83016static unsigned short ma_dr_wav_gMulawTable[256] = {
83017 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
83018 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
83019 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
83020 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,
83021 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,
83022 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,
83023 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,
83024 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,
83025 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,
83026 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,
83027 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,
83028 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,
83029 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,
83030 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,
83031 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,
83032 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
83033};
83034static MA_INLINE ma_int16 ma_dr_wav__alaw_to_s16(ma_uint8 sampleIn)
83035{
83036 return (short)ma_dr_wav_gAlawTable[sampleIn];
83037}
83038static MA_INLINE ma_int16 ma_dr_wav__mulaw_to_s16(ma_uint8 sampleIn)
83039{
83040 return (short)ma_dr_wav_gMulawTable[sampleIn];
83041}
83042MA_PRIVATE void ma_dr_wav__pcm_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
83043{
83044 size_t i;
83045 if (bytesPerSample == 1) {
83046 ma_dr_wav_u8_to_s16(pOut, pIn, totalSampleCount);
83047 return;
83048 }
83049 if (bytesPerSample == 2) {
83050 for (i = 0; i < totalSampleCount; ++i) {
83051 *pOut++ = ((const ma_int16*)pIn)[i];
83052 }
83053 return;
83054 }
83055 if (bytesPerSample == 3) {
83056 ma_dr_wav_s24_to_s16(pOut, pIn, totalSampleCount);
83057 return;
83058 }
83059 if (bytesPerSample == 4) {
83060 ma_dr_wav_s32_to_s16(pOut, (const ma_int32*)pIn, totalSampleCount);
83061 return;
83062 }
83063 if (bytesPerSample > 8) {
83064 MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
83065 return;
83066 }
83067 for (i = 0; i < totalSampleCount; ++i) {
83068 ma_uint64 sample = 0;
83069 unsigned int shift = (8 - bytesPerSample) * 8;
83070 unsigned int j;
83071 for (j = 0; j < bytesPerSample; j += 1) {
83072 MA_DR_WAV_ASSERT(j < 8);
83073 sample |= (ma_uint64)(pIn[j]) << shift;
83074 shift += 8;
83075 }
83076 pIn += j;
83077 *pOut++ = (ma_int16)((ma_int64)sample >> 48);
83078 }
83079}
83080MA_PRIVATE void ma_dr_wav__ieee_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
83081{
83082 if (bytesPerSample == 4) {
83083 ma_dr_wav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount);
83084 return;
83085 } else if (bytesPerSample == 8) {
83086 ma_dr_wav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount);
83087 return;
83088 } else {
83089 MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
83090 return;
83091 }
83092}
83093MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
83094{
83095 ma_uint64 totalFramesRead;
83096 ma_uint8 sampleData[4096] = {0};
83097 ma_uint32 bytesPerFrame;
83098 ma_uint32 bytesPerSample;
83099 ma_uint64 samplesRead;
83100 if ((pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) {
83101 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut);
83102 }
83103 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
83104 if (bytesPerFrame == 0) {
83105 return 0;
83106 }
83107 bytesPerSample = bytesPerFrame / pWav->channels;
83108 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
83109 return 0;
83110 }
83111 totalFramesRead = 0;
83112 while (framesToRead > 0) {
83113 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
83114 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
83115 if (framesRead == 0) {
83116 break;
83117 }
83118 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
83119 samplesRead = framesRead * pWav->channels;
83120 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
83121 MA_DR_WAV_ASSERT(MA_FALSE);
83122 break;
83123 }
83124 ma_dr_wav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
83125 pBufferOut += samplesRead;
83126 framesToRead -= framesRead;
83127 totalFramesRead += framesRead;
83128 }
83129 return totalFramesRead;
83130}
83131MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
83132{
83133 ma_uint64 totalFramesRead;
83134 ma_uint8 sampleData[4096] = {0};
83135 ma_uint32 bytesPerFrame;
83136 ma_uint32 bytesPerSample;
83137 ma_uint64 samplesRead;
83138 if (pBufferOut == NULL) {
83139 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
83140 }
83141 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
83142 if (bytesPerFrame == 0) {
83143 return 0;
83144 }
83145 bytesPerSample = bytesPerFrame / pWav->channels;
83146 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
83147 return 0;
83148 }
83149 totalFramesRead = 0;
83150 while (framesToRead > 0) {
83151 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
83152 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
83153 if (framesRead == 0) {
83154 break;
83155 }
83156 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
83157 samplesRead = framesRead * pWav->channels;
83158 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
83159 MA_DR_WAV_ASSERT(MA_FALSE);
83160 break;
83161 }
83162 ma_dr_wav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
83163 pBufferOut += samplesRead;
83164 framesToRead -= framesRead;
83165 totalFramesRead += framesRead;
83166 }
83167 return totalFramesRead;
83168}
83169MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
83170{
83171 ma_uint64 totalFramesRead;
83172 ma_uint8 sampleData[4096] = {0};
83173 ma_uint32 bytesPerFrame;
83174 ma_uint32 bytesPerSample;
83175 ma_uint64 samplesRead;
83176 if (pBufferOut == NULL) {
83177 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
83178 }
83179 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
83180 if (bytesPerFrame == 0) {
83181 return 0;
83182 }
83183 bytesPerSample = bytesPerFrame / pWav->channels;
83184 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
83185 return 0;
83186 }
83187 totalFramesRead = 0;
83188 while (framesToRead > 0) {
83189 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
83190 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
83191 if (framesRead == 0) {
83192 break;
83193 }
83194 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
83195 samplesRead = framesRead * pWav->channels;
83196 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
83197 MA_DR_WAV_ASSERT(MA_FALSE);
83198 break;
83199 }
83200 ma_dr_wav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
83201 #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
83202 {
83203 if (pWav->container == ma_dr_wav_container_aiff) {
83204 ma_uint64 iSample;
83205 for (iSample = 0; iSample < samplesRead; iSample += 1) {
83206 pBufferOut[iSample] = -pBufferOut[iSample];
83207 }
83208 }
83209 }
83210 #endif
83211 pBufferOut += samplesRead;
83212 framesToRead -= framesRead;
83213 totalFramesRead += framesRead;
83214 }
83215 return totalFramesRead;
83216}
83217MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
83218{
83219 ma_uint64 totalFramesRead;
83220 ma_uint8 sampleData[4096] = {0};
83221 ma_uint32 bytesPerFrame;
83222 ma_uint32 bytesPerSample;
83223 ma_uint64 samplesRead;
83224 if (pBufferOut == NULL) {
83225 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
83226 }
83227 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
83228 if (bytesPerFrame == 0) {
83229 return 0;
83230 }
83231 bytesPerSample = bytesPerFrame / pWav->channels;
83232 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
83233 return 0;
83234 }
83235 totalFramesRead = 0;
83236 while (framesToRead > 0) {
83237 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
83238 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
83239 if (framesRead == 0) {
83240 break;
83241 }
83242 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
83243 samplesRead = framesRead * pWav->channels;
83244 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
83245 MA_DR_WAV_ASSERT(MA_FALSE);
83246 break;
83247 }
83248 ma_dr_wav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
83249 #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
83250 {
83251 if (pWav->container == ma_dr_wav_container_aiff) {
83252 ma_uint64 iSample;
83253 for (iSample = 0; iSample < samplesRead; iSample += 1) {
83254 pBufferOut[iSample] = -pBufferOut[iSample];
83255 }
83256 }
83257 }
83258 #endif
83259 pBufferOut += samplesRead;
83260 framesToRead -= framesRead;
83261 totalFramesRead += framesRead;
83262 }
83263 return totalFramesRead;
83264}
83265MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
83266{
83267 if (pWav == NULL || framesToRead == 0) {
83268 return 0;
83269 }
83270 if (pBufferOut == NULL) {
83271 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
83272 }
83273 if (framesToRead * pWav->channels * sizeof(ma_int16) > MA_SIZE_MAX) {
83274 framesToRead = MA_SIZE_MAX / sizeof(ma_int16) / pWav->channels;
83275 }
83276 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) {
83277 return ma_dr_wav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut);
83278 }
83279 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) {
83280 return ma_dr_wav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut);
83281 }
83282 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) {
83283 return ma_dr_wav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut);
83284 }
83285 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) {
83286 return ma_dr_wav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut);
83287 }
83288 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
83289 return ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut);
83290 }
83291 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
83292 return ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut);
83293 }
83294 return 0;
83295}
83296MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
83297{
83298 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
83299 if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) {
83300 ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
83301 }
83302 return framesRead;
83303}
83304MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
83305{
83306 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
83307 if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) {
83308 ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
83309 }
83310 return framesRead;
83311}
83312MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount)
83313{
83314 int r;
83315 size_t i;
83316 for (i = 0; i < sampleCount; ++i) {
83317 int x = pIn[i];
83318 r = x << 8;
83319 r = r - 32768;
83320 pOut[i] = (short)r;
83321 }
83322}
83323MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount)
83324{
83325 int r;
83326 size_t i;
83327 for (i = 0; i < sampleCount; ++i) {
83328 int x = ((int)(((unsigned int)(((const ma_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+2])) << 24)) >> 8;
83329 r = x >> 8;
83330 pOut[i] = (short)r;
83331 }
83332}
83333MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount)
83334{
83335 int r;
83336 size_t i;
83337 for (i = 0; i < sampleCount; ++i) {
83338 int x = pIn[i];
83339 r = x >> 16;
83340 pOut[i] = (short)r;
83341 }
83342}
83343MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount)
83344{
83345 int r;
83346 size_t i;
83347 for (i = 0; i < sampleCount; ++i) {
83348 float x = pIn[i];
83349 float c;
83350 c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
83351 c = c + 1;
83352 r = (int)(c * 32767.5f);
83353 r = r - 32768;
83354 pOut[i] = (short)r;
83355 }
83356}
83357MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount)
83358{
83359 int r;
83360 size_t i;
83361 for (i = 0; i < sampleCount; ++i) {
83362 double x = pIn[i];
83363 double c;
83364 c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
83365 c = c + 1;
83366 r = (int)(c * 32767.5);
83367 r = r - 32768;
83368 pOut[i] = (short)r;
83369 }
83370}
83371MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount)
83372{
83373 size_t i;
83374 for (i = 0; i < sampleCount; ++i) {
83375 pOut[i] = ma_dr_wav__alaw_to_s16(pIn[i]);
83376 }
83377}
83378MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount)
83379{
83380 size_t i;
83381 for (i = 0; i < sampleCount; ++i) {
83382 pOut[i] = ma_dr_wav__mulaw_to_s16(pIn[i]);
83383 }
83384}
83385MA_PRIVATE void ma_dr_wav__pcm_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
83386{
83387 unsigned int i;
83388 if (bytesPerSample == 1) {
83389 ma_dr_wav_u8_to_f32(pOut, pIn, sampleCount);
83390 return;
83391 }
83392 if (bytesPerSample == 2) {
83393 ma_dr_wav_s16_to_f32(pOut, (const ma_int16*)pIn, sampleCount);
83394 return;
83395 }
83396 if (bytesPerSample == 3) {
83397 ma_dr_wav_s24_to_f32(pOut, pIn, sampleCount);
83398 return;
83399 }
83400 if (bytesPerSample == 4) {
83401 ma_dr_wav_s32_to_f32(pOut, (const ma_int32*)pIn, sampleCount);
83402 return;
83403 }
83404 if (bytesPerSample > 8) {
83405 MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
83406 return;
83407 }
83408 for (i = 0; i < sampleCount; ++i) {
83409 ma_uint64 sample = 0;
83410 unsigned int shift = (8 - bytesPerSample) * 8;
83411 unsigned int j;
83412 for (j = 0; j < bytesPerSample; j += 1) {
83413 MA_DR_WAV_ASSERT(j < 8);
83414 sample |= (ma_uint64)(pIn[j]) << shift;
83415 shift += 8;
83416 }
83417 pIn += j;
83418 *pOut++ = (float)((ma_int64)sample / 9223372036854775807.0);
83419 }
83420}
83421MA_PRIVATE void ma_dr_wav__ieee_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
83422{
83423 if (bytesPerSample == 4) {
83424 unsigned int i;
83425 for (i = 0; i < sampleCount; ++i) {
83426 *pOut++ = ((const float*)pIn)[i];
83427 }
83428 return;
83429 } else if (bytesPerSample == 8) {
83430 ma_dr_wav_f64_to_f32(pOut, (const double*)pIn, sampleCount);
83431 return;
83432 } else {
83433 MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
83434 return;
83435 }
83436}
83437MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
83438{
83439 ma_uint64 totalFramesRead;
83440 ma_uint8 sampleData[4096] = {0};
83441 ma_uint32 bytesPerFrame;
83442 ma_uint32 bytesPerSample;
83443 ma_uint64 samplesRead;
83444 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
83445 if (bytesPerFrame == 0) {
83446 return 0;
83447 }
83448 bytesPerSample = bytesPerFrame / pWav->channels;
83449 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
83450 return 0;
83451 }
83452 totalFramesRead = 0;
83453 while (framesToRead > 0) {
83454 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
83455 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
83456 if (framesRead == 0) {
83457 break;
83458 }
83459 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
83460 samplesRead = framesRead * pWav->channels;
83461 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
83462 MA_DR_WAV_ASSERT(MA_FALSE);
83463 break;
83464 }
83465 ma_dr_wav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
83466 pBufferOut += samplesRead;
83467 framesToRead -= framesRead;
83468 totalFramesRead += framesRead;
83469 }
83470 return totalFramesRead;
83471}
83472MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
83473{
83474 ma_uint64 totalFramesRead;
83475 ma_int16 samples16[2048];
83476 totalFramesRead = 0;
83477 while (framesToRead > 0) {
83478 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels);
83479 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16);
83480 if (framesRead == 0) {
83481 break;
83482 }
83483 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
83484 ma_dr_wav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
83485 pBufferOut += framesRead*pWav->channels;
83486 framesToRead -= framesRead;
83487 totalFramesRead += framesRead;
83488 }
83489 return totalFramesRead;
83490}
83491MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
83492{
83493 ma_uint64 totalFramesRead;
83494 ma_uint8 sampleData[4096] = {0};
83495 ma_uint32 bytesPerFrame;
83496 ma_uint32 bytesPerSample;
83497 ma_uint64 samplesRead;
83498 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) {
83499 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut);
83500 }
83501 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
83502 if (bytesPerFrame == 0) {
83503 return 0;
83504 }
83505 bytesPerSample = bytesPerFrame / pWav->channels;
83506 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
83507 return 0;
83508 }
83509 totalFramesRead = 0;
83510 while (framesToRead > 0) {
83511 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
83512 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
83513 if (framesRead == 0) {
83514 break;
83515 }
83516 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
83517 samplesRead = framesRead * pWav->channels;
83518 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
83519 MA_DR_WAV_ASSERT(MA_FALSE);
83520 break;
83521 }
83522 ma_dr_wav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
83523 pBufferOut += samplesRead;
83524 framesToRead -= framesRead;
83525 totalFramesRead += framesRead;
83526 }
83527 return totalFramesRead;
83528}
83529MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
83530{
83531 ma_uint64 totalFramesRead;
83532 ma_uint8 sampleData[4096] = {0};
83533 ma_uint32 bytesPerFrame;
83534 ma_uint32 bytesPerSample;
83535 ma_uint64 samplesRead;
83536 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
83537 if (bytesPerFrame == 0) {
83538 return 0;
83539 }
83540 bytesPerSample = bytesPerFrame / pWav->channels;
83541 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
83542 return 0;
83543 }
83544 totalFramesRead = 0;
83545 while (framesToRead > 0) {
83546 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
83547 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
83548 if (framesRead == 0) {
83549 break;
83550 }
83551 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
83552 samplesRead = framesRead * pWav->channels;
83553 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
83554 MA_DR_WAV_ASSERT(MA_FALSE);
83555 break;
83556 }
83557 ma_dr_wav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
83558 #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
83559 {
83560 if (pWav->container == ma_dr_wav_container_aiff) {
83561 ma_uint64 iSample;
83562 for (iSample = 0; iSample < samplesRead; iSample += 1) {
83563 pBufferOut[iSample] = -pBufferOut[iSample];
83564 }
83565 }
83566 }
83567 #endif
83568 pBufferOut += samplesRead;
83569 framesToRead -= framesRead;
83570 totalFramesRead += framesRead;
83571 }
83572 return totalFramesRead;
83573}
83574MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
83575{
83576 ma_uint64 totalFramesRead;
83577 ma_uint8 sampleData[4096] = {0};
83578 ma_uint32 bytesPerFrame;
83579 ma_uint32 bytesPerSample;
83580 ma_uint64 samplesRead;
83581 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
83582 if (bytesPerFrame == 0) {
83583 return 0;
83584 }
83585 bytesPerSample = bytesPerFrame / pWav->channels;
83586 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
83587 return 0;
83588 }
83589 totalFramesRead = 0;
83590 while (framesToRead > 0) {
83591 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
83592 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
83593 if (framesRead == 0) {
83594 break;
83595 }
83596 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
83597 samplesRead = framesRead * pWav->channels;
83598 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
83599 MA_DR_WAV_ASSERT(MA_FALSE);
83600 break;
83601 }
83602 ma_dr_wav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
83603 #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
83604 {
83605 if (pWav->container == ma_dr_wav_container_aiff) {
83606 ma_uint64 iSample;
83607 for (iSample = 0; iSample < samplesRead; iSample += 1) {
83608 pBufferOut[iSample] = -pBufferOut[iSample];
83609 }
83610 }
83611 }
83612 #endif
83613 pBufferOut += samplesRead;
83614 framesToRead -= framesRead;
83615 totalFramesRead += framesRead;
83616 }
83617 return totalFramesRead;
83618}
83619MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
83620{
83621 if (pWav == NULL || framesToRead == 0) {
83622 return 0;
83623 }
83624 if (pBufferOut == NULL) {
83625 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
83626 }
83627 if (framesToRead * pWav->channels * sizeof(float) > MA_SIZE_MAX) {
83628 framesToRead = MA_SIZE_MAX / sizeof(float) / pWav->channels;
83629 }
83630 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) {
83631 return ma_dr_wav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut);
83632 }
83633 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
83634 return ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut);
83635 }
83636 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) {
83637 return ma_dr_wav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut);
83638 }
83639 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) {
83640 return ma_dr_wav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut);
83641 }
83642 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) {
83643 return ma_dr_wav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut);
83644 }
83645 return 0;
83646}
83647MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
83648{
83649 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
83650 if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) {
83651 ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
83652 }
83653 return framesRead;
83654}
83655MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
83656{
83657 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
83658 if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) {
83659 ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
83660 }
83661 return framesRead;
83662}
83663MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount)
83664{
83665 size_t i;
83666 if (pOut == NULL || pIn == NULL) {
83667 return;
83668 }
83669#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
83670 for (i = 0; i < sampleCount; ++i) {
83671 *pOut++ = (pIn[i] / 256.0f) * 2 - 1;
83672 }
83673#else
83674 for (i = 0; i < sampleCount; ++i) {
83675 float x = pIn[i];
83676 x = x * 0.00784313725490196078f;
83677 x = x - 1;
83678 *pOut++ = x;
83679 }
83680#endif
83681}
83682MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount)
83683{
83684 size_t i;
83685 if (pOut == NULL || pIn == NULL) {
83686 return;
83687 }
83688 for (i = 0; i < sampleCount; ++i) {
83689 *pOut++ = pIn[i] * 0.000030517578125f;
83690 }
83691}
83692MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount)
83693{
83694 size_t i;
83695 if (pOut == NULL || pIn == NULL) {
83696 return;
83697 }
83698 for (i = 0; i < sampleCount; ++i) {
83699 double x;
83700 ma_uint32 a = ((ma_uint32)(pIn[i*3+0]) << 8);
83701 ma_uint32 b = ((ma_uint32)(pIn[i*3+1]) << 16);
83702 ma_uint32 c = ((ma_uint32)(pIn[i*3+2]) << 24);
83703 x = (double)((ma_int32)(a | b | c) >> 8);
83704 *pOut++ = (float)(x * 0.00000011920928955078125);
83705 }
83706}
83707MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount)
83708{
83709 size_t i;
83710 if (pOut == NULL || pIn == NULL) {
83711 return;
83712 }
83713 for (i = 0; i < sampleCount; ++i) {
83714 *pOut++ = (float)(pIn[i] / 2147483648.0);
83715 }
83716}
83717MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount)
83718{
83719 size_t i;
83720 if (pOut == NULL || pIn == NULL) {
83721 return;
83722 }
83723 for (i = 0; i < sampleCount; ++i) {
83724 *pOut++ = (float)pIn[i];
83725 }
83726}
83727MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount)
83728{
83729 size_t i;
83730 if (pOut == NULL || pIn == NULL) {
83731 return;
83732 }
83733 for (i = 0; i < sampleCount; ++i) {
83734 *pOut++ = ma_dr_wav__alaw_to_s16(pIn[i]) / 32768.0f;
83735 }
83736}
83737MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount)
83738{
83739 size_t i;
83740 if (pOut == NULL || pIn == NULL) {
83741 return;
83742 }
83743 for (i = 0; i < sampleCount; ++i) {
83744 *pOut++ = ma_dr_wav__mulaw_to_s16(pIn[i]) / 32768.0f;
83745 }
83746}
83747MA_PRIVATE void ma_dr_wav__pcm_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
83748{
83749 unsigned int i;
83750 if (bytesPerSample == 1) {
83751 ma_dr_wav_u8_to_s32(pOut, pIn, totalSampleCount);
83752 return;
83753 }
83754 if (bytesPerSample == 2) {
83755 ma_dr_wav_s16_to_s32(pOut, (const ma_int16*)pIn, totalSampleCount);
83756 return;
83757 }
83758 if (bytesPerSample == 3) {
83759 ma_dr_wav_s24_to_s32(pOut, pIn, totalSampleCount);
83760 return;
83761 }
83762 if (bytesPerSample == 4) {
83763 for (i = 0; i < totalSampleCount; ++i) {
83764 *pOut++ = ((const ma_int32*)pIn)[i];
83765 }
83766 return;
83767 }
83768 if (bytesPerSample > 8) {
83769 MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
83770 return;
83771 }
83772 for (i = 0; i < totalSampleCount; ++i) {
83773 ma_uint64 sample = 0;
83774 unsigned int shift = (8 - bytesPerSample) * 8;
83775 unsigned int j;
83776 for (j = 0; j < bytesPerSample; j += 1) {
83777 MA_DR_WAV_ASSERT(j < 8);
83778 sample |= (ma_uint64)(pIn[j]) << shift;
83779 shift += 8;
83780 }
83781 pIn += j;
83782 *pOut++ = (ma_int32)((ma_int64)sample >> 32);
83783 }
83784}
83785MA_PRIVATE void ma_dr_wav__ieee_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
83786{
83787 if (bytesPerSample == 4) {
83788 ma_dr_wav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount);
83789 return;
83790 } else if (bytesPerSample == 8) {
83791 ma_dr_wav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount);
83792 return;
83793 } else {
83794 MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
83795 return;
83796 }
83797}
83798MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
83799{
83800 ma_uint64 totalFramesRead;
83801 ma_uint8 sampleData[4096] = {0};
83802 ma_uint32 bytesPerFrame;
83803 ma_uint32 bytesPerSample;
83804 ma_uint64 samplesRead;
83805 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) {
83806 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut);
83807 }
83808 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
83809 if (bytesPerFrame == 0) {
83810 return 0;
83811 }
83812 bytesPerSample = bytesPerFrame / pWav->channels;
83813 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
83814 return 0;
83815 }
83816 totalFramesRead = 0;
83817 while (framesToRead > 0) {
83818 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
83819 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
83820 if (framesRead == 0) {
83821 break;
83822 }
83823 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
83824 samplesRead = framesRead * pWav->channels;
83825 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
83826 MA_DR_WAV_ASSERT(MA_FALSE);
83827 break;
83828 }
83829 ma_dr_wav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
83830 pBufferOut += samplesRead;
83831 framesToRead -= framesRead;
83832 totalFramesRead += framesRead;
83833 }
83834 return totalFramesRead;
83835}
83836MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
83837{
83838 ma_uint64 totalFramesRead = 0;
83839 ma_int16 samples16[2048];
83840 while (framesToRead > 0) {
83841 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels);
83842 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16);
83843 if (framesRead == 0) {
83844 break;
83845 }
83846 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
83847 ma_dr_wav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
83848 pBufferOut += framesRead*pWav->channels;
83849 framesToRead -= framesRead;
83850 totalFramesRead += framesRead;
83851 }
83852 return totalFramesRead;
83853}
83854MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
83855{
83856 ma_uint64 totalFramesRead;
83857 ma_uint8 sampleData[4096] = {0};
83858 ma_uint32 bytesPerFrame;
83859 ma_uint32 bytesPerSample;
83860 ma_uint64 samplesRead;
83861 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
83862 if (bytesPerFrame == 0) {
83863 return 0;
83864 }
83865 bytesPerSample = bytesPerFrame / pWav->channels;
83866 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
83867 return 0;
83868 }
83869 totalFramesRead = 0;
83870 while (framesToRead > 0) {
83871 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
83872 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
83873 if (framesRead == 0) {
83874 break;
83875 }
83876 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
83877 samplesRead = framesRead * pWav->channels;
83878 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
83879 MA_DR_WAV_ASSERT(MA_FALSE);
83880 break;
83881 }
83882 ma_dr_wav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
83883 pBufferOut += samplesRead;
83884 framesToRead -= framesRead;
83885 totalFramesRead += framesRead;
83886 }
83887 return totalFramesRead;
83888}
83889MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
83890{
83891 ma_uint64 totalFramesRead;
83892 ma_uint8 sampleData[4096] = {0};
83893 ma_uint32 bytesPerFrame;
83894 ma_uint32 bytesPerSample;
83895 ma_uint64 samplesRead;
83896 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
83897 if (bytesPerFrame == 0) {
83898 return 0;
83899 }
83900 bytesPerSample = bytesPerFrame / pWav->channels;
83901 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
83902 return 0;
83903 }
83904 totalFramesRead = 0;
83905 while (framesToRead > 0) {
83906 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
83907 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
83908 if (framesRead == 0) {
83909 break;
83910 }
83911 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
83912 samplesRead = framesRead * pWav->channels;
83913 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
83914 MA_DR_WAV_ASSERT(MA_FALSE);
83915 break;
83916 }
83917 ma_dr_wav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
83918 #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
83919 {
83920 if (pWav->container == ma_dr_wav_container_aiff) {
83921 ma_uint64 iSample;
83922 for (iSample = 0; iSample < samplesRead; iSample += 1) {
83923 pBufferOut[iSample] = -pBufferOut[iSample];
83924 }
83925 }
83926 }
83927 #endif
83928 pBufferOut += samplesRead;
83929 framesToRead -= framesRead;
83930 totalFramesRead += framesRead;
83931 }
83932 return totalFramesRead;
83933}
83934MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
83935{
83936 ma_uint64 totalFramesRead;
83937 ma_uint8 sampleData[4096] = {0};
83938 ma_uint32 bytesPerFrame;
83939 ma_uint32 bytesPerSample;
83940 ma_uint64 samplesRead;
83941 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
83942 if (bytesPerFrame == 0) {
83943 return 0;
83944 }
83945 bytesPerSample = bytesPerFrame / pWav->channels;
83946 if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
83947 return 0;
83948 }
83949 totalFramesRead = 0;
83950 while (framesToRead > 0) {
83951 ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
83952 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
83953 if (framesRead == 0) {
83954 break;
83955 }
83956 MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
83957 samplesRead = framesRead * pWav->channels;
83958 if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
83959 MA_DR_WAV_ASSERT(MA_FALSE);
83960 break;
83961 }
83962 ma_dr_wav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
83963 #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
83964 {
83965 if (pWav->container == ma_dr_wav_container_aiff) {
83966 ma_uint64 iSample;
83967 for (iSample = 0; iSample < samplesRead; iSample += 1) {
83968 pBufferOut[iSample] = -pBufferOut[iSample];
83969 }
83970 }
83971 }
83972 #endif
83973 pBufferOut += samplesRead;
83974 framesToRead -= framesRead;
83975 totalFramesRead += framesRead;
83976 }
83977 return totalFramesRead;
83978}
83979MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
83980{
83981 if (pWav == NULL || framesToRead == 0) {
83982 return 0;
83983 }
83984 if (pBufferOut == NULL) {
83985 return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
83986 }
83987 if (framesToRead * pWav->channels * sizeof(ma_int32) > MA_SIZE_MAX) {
83988 framesToRead = MA_SIZE_MAX / sizeof(ma_int32) / pWav->channels;
83989 }
83990 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) {
83991 return ma_dr_wav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut);
83992 }
83993 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
83994 return ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut);
83995 }
83996 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) {
83997 return ma_dr_wav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut);
83998 }
83999 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) {
84000 return ma_dr_wav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut);
84001 }
84002 if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) {
84003 return ma_dr_wav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut);
84004 }
84005 return 0;
84006}
84007MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
84008{
84009 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
84010 if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) {
84011 ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
84012 }
84013 return framesRead;
84014}
84015MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
84016{
84017 ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
84018 if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) {
84019 ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
84020 }
84021 return framesRead;
84022}
84023MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount)
84024{
84025 size_t i;
84026 if (pOut == NULL || pIn == NULL) {
84027 return;
84028 }
84029 for (i = 0; i < sampleCount; ++i) {
84030 *pOut++ = ((int)pIn[i] - 128) << 24;
84031 }
84032}
84033MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount)
84034{
84035 size_t i;
84036 if (pOut == NULL || pIn == NULL) {
84037 return;
84038 }
84039 for (i = 0; i < sampleCount; ++i) {
84040 *pOut++ = pIn[i] << 16;
84041 }
84042}
84043MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount)
84044{
84045 size_t i;
84046 if (pOut == NULL || pIn == NULL) {
84047 return;
84048 }
84049 for (i = 0; i < sampleCount; ++i) {
84050 unsigned int s0 = pIn[i*3 + 0];
84051 unsigned int s1 = pIn[i*3 + 1];
84052 unsigned int s2 = pIn[i*3 + 2];
84053 ma_int32 sample32 = (ma_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));
84054 *pOut++ = sample32;
84055 }
84056}
84057MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount)
84058{
84059 size_t i;
84060 if (pOut == NULL || pIn == NULL) {
84061 return;
84062 }
84063 for (i = 0; i < sampleCount; ++i) {
84064 *pOut++ = (ma_int32)(2147483648.0f * pIn[i]);
84065 }
84066}
84067MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount)
84068{
84069 size_t i;
84070 if (pOut == NULL || pIn == NULL) {
84071 return;
84072 }
84073 for (i = 0; i < sampleCount; ++i) {
84074 *pOut++ = (ma_int32)(2147483648.0 * pIn[i]);
84075 }
84076}
84077MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount)
84078{
84079 size_t i;
84080 if (pOut == NULL || pIn == NULL) {
84081 return;
84082 }
84083 for (i = 0; i < sampleCount; ++i) {
84084 *pOut++ = ((ma_int32)ma_dr_wav__alaw_to_s16(pIn[i])) << 16;
84085 }
84086}
84087MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount)
84088{
84089 size_t i;
84090 if (pOut == NULL || pIn == NULL) {
84091 return;
84092 }
84093 for (i= 0; i < sampleCount; ++i) {
84094 *pOut++ = ((ma_int32)ma_dr_wav__mulaw_to_s16(pIn[i])) << 16;
84095 }
84096}
84097MA_PRIVATE ma_int16* ma_dr_wav__read_pcm_frames_and_close_s16(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount)
84098{
84099 ma_uint64 sampleDataSize;
84100 ma_int16* pSampleData;
84101 ma_uint64 framesRead;
84102 MA_DR_WAV_ASSERT(pWav != NULL);
84103 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int16);
84104 if (sampleDataSize > MA_SIZE_MAX) {
84105 ma_dr_wav_uninit(pWav);
84106 return NULL;
84107 }
84108 pSampleData = (ma_int16*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
84109 if (pSampleData == NULL) {
84110 ma_dr_wav_uninit(pWav);
84111 return NULL;
84112 }
84113 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
84114 if (framesRead != pWav->totalPCMFrameCount) {
84115 ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
84116 ma_dr_wav_uninit(pWav);
84117 return NULL;
84118 }
84119 ma_dr_wav_uninit(pWav);
84120 if (sampleRate) {
84121 *sampleRate = pWav->sampleRate;
84122 }
84123 if (channels) {
84124 *channels = pWav->channels;
84125 }
84126 if (totalFrameCount) {
84127 *totalFrameCount = pWav->totalPCMFrameCount;
84128 }
84129 return pSampleData;
84130}
84131MA_PRIVATE float* ma_dr_wav__read_pcm_frames_and_close_f32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount)
84132{
84133 ma_uint64 sampleDataSize;
84134 float* pSampleData;
84135 ma_uint64 framesRead;
84136 MA_DR_WAV_ASSERT(pWav != NULL);
84137 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float);
84138 if (sampleDataSize > MA_SIZE_MAX) {
84139 ma_dr_wav_uninit(pWav);
84140 return NULL;
84141 }
84142 pSampleData = (float*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
84143 if (pSampleData == NULL) {
84144 ma_dr_wav_uninit(pWav);
84145 return NULL;
84146 }
84147 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
84148 if (framesRead != pWav->totalPCMFrameCount) {
84149 ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
84150 ma_dr_wav_uninit(pWav);
84151 return NULL;
84152 }
84153 ma_dr_wav_uninit(pWav);
84154 if (sampleRate) {
84155 *sampleRate = pWav->sampleRate;
84156 }
84157 if (channels) {
84158 *channels = pWav->channels;
84159 }
84160 if (totalFrameCount) {
84161 *totalFrameCount = pWav->totalPCMFrameCount;
84162 }
84163 return pSampleData;
84164}
84165MA_PRIVATE ma_int32* ma_dr_wav__read_pcm_frames_and_close_s32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount)
84166{
84167 ma_uint64 sampleDataSize;
84168 ma_int32* pSampleData;
84169 ma_uint64 framesRead;
84170 MA_DR_WAV_ASSERT(pWav != NULL);
84171 sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int32);
84172 if (sampleDataSize > MA_SIZE_MAX) {
84173 ma_dr_wav_uninit(pWav);
84174 return NULL;
84175 }
84176 pSampleData = (ma_int32*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
84177 if (pSampleData == NULL) {
84178 ma_dr_wav_uninit(pWav);
84179 return NULL;
84180 }
84181 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
84182 if (framesRead != pWav->totalPCMFrameCount) {
84183 ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
84184 ma_dr_wav_uninit(pWav);
84185 return NULL;
84186 }
84187 ma_dr_wav_uninit(pWav);
84188 if (sampleRate) {
84189 *sampleRate = pWav->sampleRate;
84190 }
84191 if (channels) {
84192 *channels = pWav->channels;
84193 }
84194 if (totalFrameCount) {
84195 *totalFrameCount = pWav->totalPCMFrameCount;
84196 }
84197 return pSampleData;
84198}
84199MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
84200{
84201 ma_dr_wav wav;
84202 if (channelsOut) {
84203 *channelsOut = 0;
84204 }
84205 if (sampleRateOut) {
84206 *sampleRateOut = 0;
84207 }
84208 if (totalFrameCountOut) {
84209 *totalFrameCountOut = 0;
84210 }
84211 if (!ma_dr_wav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
84212 return NULL;
84213 }
84214 return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
84215}
84216MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
84217{
84218 ma_dr_wav wav;
84219 if (channelsOut) {
84220 *channelsOut = 0;
84221 }
84222 if (sampleRateOut) {
84223 *sampleRateOut = 0;
84224 }
84225 if (totalFrameCountOut) {
84226 *totalFrameCountOut = 0;
84227 }
84228 if (!ma_dr_wav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
84229 return NULL;
84230 }
84231 return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
84232}
84233MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
84234{
84235 ma_dr_wav wav;
84236 if (channelsOut) {
84237 *channelsOut = 0;
84238 }
84239 if (sampleRateOut) {
84240 *sampleRateOut = 0;
84241 }
84242 if (totalFrameCountOut) {
84243 *totalFrameCountOut = 0;
84244 }
84245 if (!ma_dr_wav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
84246 return NULL;
84247 }
84248 return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
84249}
84250#ifndef MA_DR_WAV_NO_STDIO
84251MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
84252{
84253 ma_dr_wav wav;
84254 if (channelsOut) {
84255 *channelsOut = 0;
84256 }
84257 if (sampleRateOut) {
84258 *sampleRateOut = 0;
84259 }
84260 if (totalFrameCountOut) {
84261 *totalFrameCountOut = 0;
84262 }
84263 if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) {
84264 return NULL;
84265 }
84266 return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
84267}
84268MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
84269{
84270 ma_dr_wav wav;
84271 if (channelsOut) {
84272 *channelsOut = 0;
84273 }
84274 if (sampleRateOut) {
84275 *sampleRateOut = 0;
84276 }
84277 if (totalFrameCountOut) {
84278 *totalFrameCountOut = 0;
84279 }
84280 if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) {
84281 return NULL;
84282 }
84283 return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
84284}
84285MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
84286{
84287 ma_dr_wav wav;
84288 if (channelsOut) {
84289 *channelsOut = 0;
84290 }
84291 if (sampleRateOut) {
84292 *sampleRateOut = 0;
84293 }
84294 if (totalFrameCountOut) {
84295 *totalFrameCountOut = 0;
84296 }
84297 if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) {
84298 return NULL;
84299 }
84300 return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
84301}
84302#ifndef MA_DR_WAV_NO_WCHAR
84303MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
84304{
84305 ma_dr_wav wav;
84306 if (sampleRateOut) {
84307 *sampleRateOut = 0;
84308 }
84309 if (channelsOut) {
84310 *channelsOut = 0;
84311 }
84312 if (totalFrameCountOut) {
84313 *totalFrameCountOut = 0;
84314 }
84315 if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) {
84316 return NULL;
84317 }
84318 return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
84319}
84320MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
84321{
84322 ma_dr_wav wav;
84323 if (sampleRateOut) {
84324 *sampleRateOut = 0;
84325 }
84326 if (channelsOut) {
84327 *channelsOut = 0;
84328 }
84329 if (totalFrameCountOut) {
84330 *totalFrameCountOut = 0;
84331 }
84332 if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) {
84333 return NULL;
84334 }
84335 return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
84336}
84337MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
84338{
84339 ma_dr_wav wav;
84340 if (sampleRateOut) {
84341 *sampleRateOut = 0;
84342 }
84343 if (channelsOut) {
84344 *channelsOut = 0;
84345 }
84346 if (totalFrameCountOut) {
84347 *totalFrameCountOut = 0;
84348 }
84349 if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) {
84350 return NULL;
84351 }
84352 return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
84353}
84354#endif
84355#endif
84356MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
84357{
84358 ma_dr_wav wav;
84359 if (channelsOut) {
84360 *channelsOut = 0;
84361 }
84362 if (sampleRateOut) {
84363 *sampleRateOut = 0;
84364 }
84365 if (totalFrameCountOut) {
84366 *totalFrameCountOut = 0;
84367 }
84368 if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
84369 return NULL;
84370 }
84371 return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
84372}
84373MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
84374{
84375 ma_dr_wav wav;
84376 if (channelsOut) {
84377 *channelsOut = 0;
84378 }
84379 if (sampleRateOut) {
84380 *sampleRateOut = 0;
84381 }
84382 if (totalFrameCountOut) {
84383 *totalFrameCountOut = 0;
84384 }
84385 if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
84386 return NULL;
84387 }
84388 return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
84389}
84390MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
84391{
84392 ma_dr_wav wav;
84393 if (channelsOut) {
84394 *channelsOut = 0;
84395 }
84396 if (sampleRateOut) {
84397 *sampleRateOut = 0;
84398 }
84399 if (totalFrameCountOut) {
84400 *totalFrameCountOut = 0;
84401 }
84402 if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
84403 return NULL;
84404 }
84405 return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
84406}
84407#endif
84408MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
84409{
84410 if (pAllocationCallbacks != NULL) {
84411 ma_dr_wav__free_from_callbacks(p, pAllocationCallbacks);
84412 } else {
84413 ma_dr_wav__free_default(p, NULL);
84414 }
84415}
84416MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data)
84417{
84418 return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8);
84419}
84420MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data)
84421{
84422 return (ma_int16)ma_dr_wav_bytes_to_u16(data);
84423}
84424MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data)
84425{
84426 return ma_dr_wav_bytes_to_u32_le(data);
84427}
84428MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data)
84429{
84430 union {
84431 ma_uint32 u32;
84432 float f32;
84433 } value;
84434 value.u32 = ma_dr_wav_bytes_to_u32(data);
84435 return value.f32;
84436}
84437MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data)
84438{
84439 return (ma_int32)ma_dr_wav_bytes_to_u32(data);
84440}
84441MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data)
84442{
84443 return
84444 ((ma_uint64)data[0] << 0) | ((ma_uint64)data[1] << 8) | ((ma_uint64)data[2] << 16) | ((ma_uint64)data[3] << 24) |
84445 ((ma_uint64)data[4] << 32) | ((ma_uint64)data[5] << 40) | ((ma_uint64)data[6] << 48) | ((ma_uint64)data[7] << 56);
84446}
84447MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data)
84448{
84449 return (ma_int64)ma_dr_wav_bytes_to_u64(data);
84450}
84451MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16])
84452{
84453 int i;
84454 for (i = 0; i < 16; i += 1) {
84455 if (a[i] != b[i]) {
84456 return MA_FALSE;
84457 }
84458 }
84459 return MA_TRUE;
84460}
84461MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b)
84462{
84463 return
84464 a[0] == b[0] &&
84465 a[1] == b[1] &&
84466 a[2] == b[2] &&
84467 a[3] == b[3];
84468}
84469#ifdef __MRC__
84470#pragma options opt reset
84471#endif
84472#endif
84473/* dr_wav_c end */
84474#endif /* MA_DR_WAV_IMPLEMENTATION */
84475#endif /* MA_NO_WAV */
84476
84477#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
84478#if !defined(MA_DR_FLAC_IMPLEMENTATION)
84479/* dr_flac_c begin */
84480#ifndef ma_dr_flac_c
84481#define ma_dr_flac_c
84482#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
84483 #pragma GCC diagnostic push
84484 #if __GNUC__ >= 7
84485 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
84486 #endif
84487#endif
84488#ifdef __linux__
84489 #ifndef _BSD_SOURCE
84490 #define _BSD_SOURCE
84491 #endif
84492 #ifndef _DEFAULT_SOURCE
84493 #define _DEFAULT_SOURCE
84494 #endif
84495 #ifndef __USE_BSD
84496 #define __USE_BSD
84497 #endif
84498 #include <endian.h>
84499#endif
84500#include <stdlib.h>
84501#include <string.h>
84502#if !defined(MA_DR_FLAC_NO_SIMD)
84503 #if defined(MA_X64) || defined(MA_X86)
84504 #if defined(_MSC_VER) && !defined(__clang__)
84505 #if _MSC_VER >= 1400 && !defined(MA_DR_FLAC_NO_SSE2)
84506 #define MA_DR_FLAC_SUPPORT_SSE2
84507 #endif
84508 #if _MSC_VER >= 1600 && !defined(MA_DR_FLAC_NO_SSE41)
84509 #define MA_DR_FLAC_SUPPORT_SSE41
84510 #endif
84511 #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
84512 #if defined(__SSE2__) && !defined(MA_DR_FLAC_NO_SSE2)
84513 #define MA_DR_FLAC_SUPPORT_SSE2
84514 #endif
84515 #if defined(__SSE4_1__) && !defined(MA_DR_FLAC_NO_SSE41)
84516 #define MA_DR_FLAC_SUPPORT_SSE41
84517 #endif
84518 #endif
84519 #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
84520 #if !defined(MA_DR_FLAC_SUPPORT_SSE2) && !defined(MA_DR_FLAC_NO_SSE2) && __has_include(<emmintrin.h>)
84521 #define MA_DR_FLAC_SUPPORT_SSE2
84522 #endif
84523 #if !defined(MA_DR_FLAC_SUPPORT_SSE41) && !defined(MA_DR_FLAC_NO_SSE41) && __has_include(<smmintrin.h>)
84524 #define MA_DR_FLAC_SUPPORT_SSE41
84525 #endif
84526 #endif
84527 #if defined(MA_DR_FLAC_SUPPORT_SSE41)
84528 #include <smmintrin.h>
84529 #elif defined(MA_DR_FLAC_SUPPORT_SSE2)
84530 #include <emmintrin.h>
84531 #endif
84532 #endif
84533 #if defined(MA_ARM)
84534 #if !defined(MA_DR_FLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
84535 #define MA_DR_FLAC_SUPPORT_NEON
84536 #include <arm_neon.h>
84537 #endif
84538 #endif
84539#endif
84540#if !defined(MA_DR_FLAC_NO_SIMD) && (defined(MA_X86) || defined(MA_X64))
84541 #if defined(_MSC_VER) && !defined(__clang__)
84542 #if _MSC_VER >= 1400
84543 #include <intrin.h>
84544 static void ma_dr_flac__cpuid(int info[4], int fid)
84545 {
84546 __cpuid(info, fid);
84547 }
84548 #else
84549 #define MA_DR_FLAC_NO_CPUID
84550 #endif
84551 #else
84552 #if defined(__GNUC__) || defined(__clang__)
84553 static void ma_dr_flac__cpuid(int info[4], int fid)
84554 {
84555 #if defined(MA_X86) && defined(__PIC__)
84556 __asm__ __volatile__ (
84557 "xchg{l} {%%}ebx, %k1;"
84558 "cpuid;"
84559 "xchg{l} {%%}ebx, %k1;"
84560 : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
84561 );
84562 #else
84563 __asm__ __volatile__ (
84564 "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
84565 );
84566 #endif
84567 }
84568 #else
84569 #define MA_DR_FLAC_NO_CPUID
84570 #endif
84571 #endif
84572#else
84573 #define MA_DR_FLAC_NO_CPUID
84574#endif
84575static MA_INLINE ma_bool32 ma_dr_flac_has_sse2(void)
84576{
84577#if defined(MA_DR_FLAC_SUPPORT_SSE2)
84578 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE2)
84579 #if defined(MA_X64)
84580 return MA_TRUE;
84581 #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
84582 return MA_TRUE;
84583 #else
84584 #if defined(MA_DR_FLAC_NO_CPUID)
84585 return MA_FALSE;
84586 #else
84587 int info[4];
84588 ma_dr_flac__cpuid(info, 1);
84589 return (info[3] & (1 << 26)) != 0;
84590 #endif
84591 #endif
84592 #else
84593 return MA_FALSE;
84594 #endif
84595#else
84596 return MA_FALSE;
84597#endif
84598}
84599static MA_INLINE ma_bool32 ma_dr_flac_has_sse41(void)
84600{
84601#if defined(MA_DR_FLAC_SUPPORT_SSE41)
84602 #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE41)
84603 #if defined(__SSE4_1__) || defined(__AVX__)
84604 return MA_TRUE;
84605 #else
84606 #if defined(MA_DR_FLAC_NO_CPUID)
84607 return MA_FALSE;
84608 #else
84609 int info[4];
84610 ma_dr_flac__cpuid(info, 1);
84611 return (info[2] & (1 << 19)) != 0;
84612 #endif
84613 #endif
84614 #else
84615 return MA_FALSE;
84616 #endif
84617#else
84618 return MA_FALSE;
84619#endif
84620}
84621#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(MA_X86) || defined(MA_X64)) && !defined(__clang__)
84622 #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC
84623#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
84624 #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC
84625#elif defined(__clang__)
84626 #if defined(__has_builtin)
84627 #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl)
84628 #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC
84629 #endif
84630 #endif
84631#endif
84632#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__)
84633 #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC
84634 #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC
84635 #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC
84636#elif defined(__clang__)
84637 #if defined(__has_builtin)
84638 #if __has_builtin(__builtin_bswap16)
84639 #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC
84640 #endif
84641 #if __has_builtin(__builtin_bswap32)
84642 #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC
84643 #endif
84644 #if __has_builtin(__builtin_bswap64)
84645 #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC
84646 #endif
84647 #endif
84648#elif defined(__GNUC__)
84649 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
84650 #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC
84651 #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC
84652 #endif
84653 #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
84654 #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC
84655 #endif
84656#elif defined(__WATCOMC__) && defined(__386__)
84657 #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC
84658 #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC
84659 #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC
84660 extern __inline ma_uint16 _watcom_bswap16(ma_uint16);
84661 extern __inline ma_uint32 _watcom_bswap32(ma_uint32);
84662 extern __inline ma_uint64 _watcom_bswap64(ma_uint64);
84663#pragma aux _watcom_bswap16 = \
84664 "xchg al, ah" \
84665 parm [ax] \
84666 value [ax] \
84667 modify nomemory;
84668#pragma aux _watcom_bswap32 = \
84669 "bswap eax" \
84670 parm [eax] \
84671 value [eax] \
84672 modify nomemory;
84673#pragma aux _watcom_bswap64 = \
84674 "bswap eax" \
84675 "bswap edx" \
84676 "xchg eax,edx" \
84677 parm [eax edx] \
84678 value [eax edx] \
84679 modify nomemory;
84680#endif
84681#ifndef MA_DR_FLAC_ASSERT
84682#include <assert.h>
84683#define MA_DR_FLAC_ASSERT(expression) assert(expression)
84684#endif
84685#ifndef MA_DR_FLAC_MALLOC
84686#define MA_DR_FLAC_MALLOC(sz) malloc((sz))
84687#endif
84688#ifndef MA_DR_FLAC_REALLOC
84689#define MA_DR_FLAC_REALLOC(p, sz) realloc((p), (sz))
84690#endif
84691#ifndef MA_DR_FLAC_FREE
84692#define MA_DR_FLAC_FREE(p) free((p))
84693#endif
84694#ifndef MA_DR_FLAC_COPY_MEMORY
84695#define MA_DR_FLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
84696#endif
84697#ifndef MA_DR_FLAC_ZERO_MEMORY
84698#define MA_DR_FLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
84699#endif
84700#ifndef MA_DR_FLAC_ZERO_OBJECT
84701#define MA_DR_FLAC_ZERO_OBJECT(p) MA_DR_FLAC_ZERO_MEMORY((p), sizeof(*(p)))
84702#endif
84703#define MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE 64
84704#define MA_DR_FLAC_SUBFRAME_CONSTANT 0
84705#define MA_DR_FLAC_SUBFRAME_VERBATIM 1
84706#define MA_DR_FLAC_SUBFRAME_FIXED 8
84707#define MA_DR_FLAC_SUBFRAME_LPC 32
84708#define MA_DR_FLAC_SUBFRAME_RESERVED 255
84709#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0
84710#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1
84711#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0
84712#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8
84713#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9
84714#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10
84715#define MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES 18
84716#define MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36
84717#define MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12
84718#define ma_dr_flac_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
84719MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)
84720{
84721 if (pMajor) {
84722 *pMajor = MA_DR_FLAC_VERSION_MAJOR;
84723 }
84724 if (pMinor) {
84725 *pMinor = MA_DR_FLAC_VERSION_MINOR;
84726 }
84727 if (pRevision) {
84728 *pRevision = MA_DR_FLAC_VERSION_REVISION;
84729 }
84730}
84731MA_API const char* ma_dr_flac_version_string(void)
84732{
84733 return MA_DR_FLAC_VERSION_STRING;
84734}
84735#if defined(__has_feature)
84736 #if __has_feature(thread_sanitizer)
84737 #define MA_DR_FLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread")))
84738 #else
84739 #define MA_DR_FLAC_NO_THREAD_SANITIZE
84740 #endif
84741#else
84742 #define MA_DR_FLAC_NO_THREAD_SANITIZE
84743#endif
84744#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC)
84745static ma_bool32 ma_dr_flac__gIsLZCNTSupported = MA_FALSE;
84746#endif
84747#ifndef MA_DR_FLAC_NO_CPUID
84748static ma_bool32 ma_dr_flac__gIsSSE2Supported = MA_FALSE;
84749static ma_bool32 ma_dr_flac__gIsSSE41Supported = MA_FALSE;
84750MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void)
84751{
84752 static ma_bool32 isCPUCapsInitialized = MA_FALSE;
84753 if (!isCPUCapsInitialized) {
84754#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC)
84755 int info[4] = {0};
84756 ma_dr_flac__cpuid(info, 0x80000001);
84757 ma_dr_flac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0;
84758#endif
84759 ma_dr_flac__gIsSSE2Supported = ma_dr_flac_has_sse2();
84760 ma_dr_flac__gIsSSE41Supported = ma_dr_flac_has_sse41();
84761 isCPUCapsInitialized = MA_TRUE;
84762 }
84763}
84764#else
84765static ma_bool32 ma_dr_flac__gIsNEONSupported = MA_FALSE;
84766static MA_INLINE ma_bool32 ma_dr_flac__has_neon(void)
84767{
84768#if defined(MA_DR_FLAC_SUPPORT_NEON)
84769 #if defined(MA_ARM) && !defined(MA_DR_FLAC_NO_NEON)
84770 #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
84771 return MA_TRUE;
84772 #else
84773 return MA_FALSE;
84774 #endif
84775 #else
84776 return MA_FALSE;
84777 #endif
84778#else
84779 return MA_FALSE;
84780#endif
84781}
84782MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void)
84783{
84784 ma_dr_flac__gIsNEONSupported = ma_dr_flac__has_neon();
84785#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
84786 ma_dr_flac__gIsLZCNTSupported = MA_TRUE;
84787#endif
84788}
84789#endif
84790static MA_INLINE ma_bool32 ma_dr_flac__is_little_endian(void)
84791{
84792#if defined(MA_X86) || defined(MA_X64)
84793 return MA_TRUE;
84794#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
84795 return MA_TRUE;
84796#else
84797 int n = 1;
84798 return (*(char*)&n) == 1;
84799#endif
84800}
84801static MA_INLINE ma_uint16 ma_dr_flac__swap_endian_uint16(ma_uint16 n)
84802{
84803#ifdef MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC
84804 #if defined(_MSC_VER) && !defined(__clang__)
84805 return _byteswap_ushort(n);
84806 #elif defined(__GNUC__) || defined(__clang__)
84807 return __builtin_bswap16(n);
84808 #elif defined(__WATCOMC__) && defined(__386__)
84809 return _watcom_bswap16(n);
84810 #else
84811 #error "This compiler does not support the byte swap intrinsic."
84812 #endif
84813#else
84814 return ((n & 0xFF00) >> 8) |
84815 ((n & 0x00FF) << 8);
84816#endif
84817}
84818static MA_INLINE ma_uint32 ma_dr_flac__swap_endian_uint32(ma_uint32 n)
84819{
84820#ifdef MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC
84821 #if defined(_MSC_VER) && !defined(__clang__)
84822 return _byteswap_ulong(n);
84823 #elif defined(__GNUC__) || defined(__clang__)
84824 #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT)
84825 ma_uint32 r;
84826 __asm__ __volatile__ (
84827 #if defined(MA_64BIT)
84828 "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n)
84829 #else
84830 "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
84831 #endif
84832 );
84833 return r;
84834 #else
84835 return __builtin_bswap32(n);
84836 #endif
84837 #elif defined(__WATCOMC__) && defined(__386__)
84838 return _watcom_bswap32(n);
84839 #else
84840 #error "This compiler does not support the byte swap intrinsic."
84841 #endif
84842#else
84843 return ((n & 0xFF000000) >> 24) |
84844 ((n & 0x00FF0000) >> 8) |
84845 ((n & 0x0000FF00) << 8) |
84846 ((n & 0x000000FF) << 24);
84847#endif
84848}
84849static MA_INLINE ma_uint64 ma_dr_flac__swap_endian_uint64(ma_uint64 n)
84850{
84851#ifdef MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC
84852 #if defined(_MSC_VER) && !defined(__clang__)
84853 return _byteswap_uint64(n);
84854 #elif defined(__GNUC__) || defined(__clang__)
84855 return __builtin_bswap64(n);
84856 #elif defined(__WATCOMC__) && defined(__386__)
84857 return _watcom_bswap64(n);
84858 #else
84859 #error "This compiler does not support the byte swap intrinsic."
84860 #endif
84861#else
84862 return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) |
84863 ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) |
84864 ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) |
84865 ((n & ((ma_uint64)0x000000FF << 32)) >> 8) |
84866 ((n & ((ma_uint64)0xFF000000 )) << 8) |
84867 ((n & ((ma_uint64)0x00FF0000 )) << 24) |
84868 ((n & ((ma_uint64)0x0000FF00 )) << 40) |
84869 ((n & ((ma_uint64)0x000000FF )) << 56);
84870#endif
84871}
84872static MA_INLINE ma_uint16 ma_dr_flac__be2host_16(ma_uint16 n)
84873{
84874 if (ma_dr_flac__is_little_endian()) {
84875 return ma_dr_flac__swap_endian_uint16(n);
84876 }
84877 return n;
84878}
84879static MA_INLINE ma_uint32 ma_dr_flac__be2host_32(ma_uint32 n)
84880{
84881 if (ma_dr_flac__is_little_endian()) {
84882 return ma_dr_flac__swap_endian_uint32(n);
84883 }
84884 return n;
84885}
84886static MA_INLINE ma_uint32 ma_dr_flac__be2host_32_ptr_unaligned(const void* pData)
84887{
84888 const ma_uint8* pNum = (ma_uint8*)pData;
84889 return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3);
84890}
84891static MA_INLINE ma_uint64 ma_dr_flac__be2host_64(ma_uint64 n)
84892{
84893 if (ma_dr_flac__is_little_endian()) {
84894 return ma_dr_flac__swap_endian_uint64(n);
84895 }
84896 return n;
84897}
84898static MA_INLINE ma_uint32 ma_dr_flac__le2host_32(ma_uint32 n)
84899{
84900 if (!ma_dr_flac__is_little_endian()) {
84901 return ma_dr_flac__swap_endian_uint32(n);
84902 }
84903 return n;
84904}
84905static MA_INLINE ma_uint32 ma_dr_flac__le2host_32_ptr_unaligned(const void* pData)
84906{
84907 const ma_uint8* pNum = (ma_uint8*)pData;
84908 return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24;
84909}
84910static MA_INLINE ma_uint32 ma_dr_flac__unsynchsafe_32(ma_uint32 n)
84911{
84912 ma_uint32 result = 0;
84913 result |= (n & 0x7F000000) >> 3;
84914 result |= (n & 0x007F0000) >> 2;
84915 result |= (n & 0x00007F00) >> 1;
84916 result |= (n & 0x0000007F) >> 0;
84917 return result;
84918}
84919static ma_uint8 ma_dr_flac__crc8_table[] = {
84920 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
84921 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
84922 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
84923 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
84924 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
84925 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
84926 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
84927 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
84928 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
84929 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
84930 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
84931 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
84932 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
84933 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
84934 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
84935 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
84936};
84937static ma_uint16 ma_dr_flac__crc16_table[] = {
84938 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
84939 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
84940 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
84941 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
84942 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
84943 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
84944 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
84945 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
84946 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
84947 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
84948 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
84949 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
84950 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
84951 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
84952 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
84953 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
84954 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
84955 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
84956 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
84957 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
84958 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
84959 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
84960 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
84961 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
84962 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
84963 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
84964 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
84965 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
84966 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
84967 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
84968 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
84969 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202
84970};
84971static MA_INLINE ma_uint8 ma_dr_flac_crc8_byte(ma_uint8 crc, ma_uint8 data)
84972{
84973 return ma_dr_flac__crc8_table[crc ^ data];
84974}
84975static MA_INLINE ma_uint8 ma_dr_flac_crc8(ma_uint8 crc, ma_uint32 data, ma_uint32 count)
84976{
84977#ifdef MA_DR_FLAC_NO_CRC
84978 (void)crc;
84979 (void)data;
84980 (void)count;
84981 return 0;
84982#else
84983#if 0
84984 ma_uint8 p = 0x07;
84985 for (int i = count-1; i >= 0; --i) {
84986 ma_uint8 bit = (data & (1 << i)) >> i;
84987 if (crc & 0x80) {
84988 crc = ((crc << 1) | bit) ^ p;
84989 } else {
84990 crc = ((crc << 1) | bit);
84991 }
84992 }
84993 return crc;
84994#else
84995 ma_uint32 wholeBytes;
84996 ma_uint32 leftoverBits;
84997 ma_uint64 leftoverDataMask;
84998 static ma_uint64 leftoverDataMaskTable[8] = {
84999 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
85000 };
85001 MA_DR_FLAC_ASSERT(count <= 32);
85002 wholeBytes = count >> 3;
85003 leftoverBits = count - (wholeBytes*8);
85004 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
85005 switch (wholeBytes) {
85006 case 4: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
85007 case 3: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
85008 case 2: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
85009 case 1: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
85010 case 0: if (leftoverBits > 0) crc = (ma_uint8)((crc << leftoverBits) ^ ma_dr_flac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]);
85011 }
85012 return crc;
85013#endif
85014#endif
85015}
85016static MA_INLINE ma_uint16 ma_dr_flac_crc16_byte(ma_uint16 crc, ma_uint8 data)
85017{
85018 return (crc << 8) ^ ma_dr_flac__crc16_table[(ma_uint8)(crc >> 8) ^ data];
85019}
85020static MA_INLINE ma_uint16 ma_dr_flac_crc16_cache(ma_uint16 crc, ma_dr_flac_cache_t data)
85021{
85022#ifdef MA_64BIT
85023 crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF));
85024 crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF));
85025 crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF));
85026 crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF));
85027#endif
85028 crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF));
85029 crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF));
85030 crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF));
85031 crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF));
85032 return crc;
85033}
85034static MA_INLINE ma_uint16 ma_dr_flac_crc16_bytes(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 byteCount)
85035{
85036 switch (byteCount)
85037 {
85038#ifdef MA_64BIT
85039 case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF));
85040 case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF));
85041 case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF));
85042 case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF));
85043#endif
85044 case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF));
85045 case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF));
85046 case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF));
85047 case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF));
85048 }
85049 return crc;
85050}
85051#if 0
85052static MA_INLINE ma_uint16 ma_dr_flac_crc16__32bit(ma_uint16 crc, ma_uint32 data, ma_uint32 count)
85053{
85054#ifdef MA_DR_FLAC_NO_CRC
85055 (void)crc;
85056 (void)data;
85057 (void)count;
85058 return 0;
85059#else
85060#if 0
85061 ma_uint16 p = 0x8005;
85062 for (int i = count-1; i >= 0; --i) {
85063 ma_uint16 bit = (data & (1ULL << i)) >> i;
85064 if (r & 0x8000) {
85065 r = ((r << 1) | bit) ^ p;
85066 } else {
85067 r = ((r << 1) | bit);
85068 }
85069 }
85070 return crc;
85071#else
85072 ma_uint32 wholeBytes;
85073 ma_uint32 leftoverBits;
85074 ma_uint64 leftoverDataMask;
85075 static ma_uint64 leftoverDataMaskTable[8] = {
85076 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
85077 };
85078 MA_DR_FLAC_ASSERT(count <= 64);
85079 wholeBytes = count >> 3;
85080 leftoverBits = count & 7;
85081 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
85082 switch (wholeBytes) {
85083 default:
85084 case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
85085 case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
85086 case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
85087 case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
85088 case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
85089 }
85090 return crc;
85091#endif
85092#endif
85093}
85094static MA_INLINE ma_uint16 ma_dr_flac_crc16__64bit(ma_uint16 crc, ma_uint64 data, ma_uint32 count)
85095{
85096#ifdef MA_DR_FLAC_NO_CRC
85097 (void)crc;
85098 (void)data;
85099 (void)count;
85100 return 0;
85101#else
85102 ma_uint32 wholeBytes;
85103 ma_uint32 leftoverBits;
85104 ma_uint64 leftoverDataMask;
85105 static ma_uint64 leftoverDataMaskTable[8] = {
85106 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
85107 };
85108 MA_DR_FLAC_ASSERT(count <= 64);
85109 wholeBytes = count >> 3;
85110 leftoverBits = count & 7;
85111 leftoverDataMask = leftoverDataMaskTable[leftoverBits];
85112 switch (wholeBytes) {
85113 default:
85114 case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits)));
85115 case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits)));
85116 case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits)));
85117 case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits)));
85118 case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits)));
85119 case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits)));
85120 case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits)));
85121 case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits)));
85122 case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
85123 }
85124 return crc;
85125#endif
85126}
85127static MA_INLINE ma_uint16 ma_dr_flac_crc16(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 count)
85128{
85129#ifdef MA_64BIT
85130 return ma_dr_flac_crc16__64bit(crc, data, count);
85131#else
85132 return ma_dr_flac_crc16__32bit(crc, data, count);
85133#endif
85134}
85135#endif
85136#ifdef MA_64BIT
85137#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_64
85138#else
85139#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_32
85140#endif
85141#define MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache))
85142#define MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8)
85143#define MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits)
85144#define MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(ma_dr_flac_cache_t)0) >> (_bitCount)))
85145#define MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount))
85146#define MA_DR_FLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount))
85147#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)))
85148#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1)))
85149#define MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2))
85150#define MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) (MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0]))
85151#define MA_DR_FLAC_CACHE_L2_LINES_REMAINING(bs) (MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line)
85152#ifndef MA_DR_FLAC_NO_CRC
85153static MA_INLINE void ma_dr_flac__reset_crc16(ma_dr_flac_bs* bs)
85154{
85155 bs->crc16 = 0;
85156 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
85157}
85158static MA_INLINE void ma_dr_flac__update_crc16(ma_dr_flac_bs* bs)
85159{
85160 if (bs->crc16CacheIgnoredBytes == 0) {
85161 bs->crc16 = ma_dr_flac_crc16_cache(bs->crc16, bs->crc16Cache);
85162 } else {
85163 bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache, MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes);
85164 bs->crc16CacheIgnoredBytes = 0;
85165 }
85166}
85167static MA_INLINE ma_uint16 ma_dr_flac__flush_crc16(ma_dr_flac_bs* bs)
85168{
85169 MA_DR_FLAC_ASSERT((MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0);
85170 if (MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) == 0) {
85171 ma_dr_flac__update_crc16(bs);
85172 } else {
85173 bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache >> MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes);
85174 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
85175 }
85176 return bs->crc16;
85177}
85178#endif
85179static MA_INLINE ma_bool32 ma_dr_flac__reload_l1_cache_from_l2(ma_dr_flac_bs* bs)
85180{
85181 size_t bytesRead;
85182 size_t alignedL1LineCount;
85183 if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
85184 bs->cache = bs->cacheL2[bs->nextL2Line++];
85185 return MA_TRUE;
85186 }
85187 if (bs->unalignedByteCount > 0) {
85188 return MA_FALSE;
85189 }
85190 bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs));
85191 bs->nextL2Line = 0;
85192 if (bytesRead == MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)) {
85193 bs->cache = bs->cacheL2[bs->nextL2Line++];
85194 return MA_TRUE;
85195 }
85196 alignedL1LineCount = bytesRead / MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs);
85197 bs->unalignedByteCount = bytesRead - (alignedL1LineCount * MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs));
85198 if (bs->unalignedByteCount > 0) {
85199 bs->unalignedCache = bs->cacheL2[alignedL1LineCount];
85200 }
85201 if (alignedL1LineCount > 0) {
85202 size_t offset = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount;
85203 size_t i;
85204 for (i = alignedL1LineCount; i > 0; --i) {
85205 bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1];
85206 }
85207 bs->nextL2Line = (ma_uint32)offset;
85208 bs->cache = bs->cacheL2[bs->nextL2Line++];
85209 return MA_TRUE;
85210 } else {
85211 bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs);
85212 return MA_FALSE;
85213 }
85214}
85215static ma_bool32 ma_dr_flac__reload_cache(ma_dr_flac_bs* bs)
85216{
85217 size_t bytesRead;
85218#ifndef MA_DR_FLAC_NO_CRC
85219 ma_dr_flac__update_crc16(bs);
85220#endif
85221 if (ma_dr_flac__reload_l1_cache_from_l2(bs)) {
85222 bs->cache = ma_dr_flac__be2host__cache_line(bs->cache);
85223 bs->consumedBits = 0;
85224#ifndef MA_DR_FLAC_NO_CRC
85225 bs->crc16Cache = bs->cache;
85226#endif
85227 return MA_TRUE;
85228 }
85229 bytesRead = bs->unalignedByteCount;
85230 if (bytesRead == 0) {
85231 bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
85232 return MA_FALSE;
85233 }
85234 MA_DR_FLAC_ASSERT(bytesRead < MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs));
85235 bs->consumedBits = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8;
85236 bs->cache = ma_dr_flac__be2host__cache_line(bs->unalignedCache);
85237 bs->cache &= MA_DR_FLAC_CACHE_L1_SELECTION_MASK(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs));
85238 bs->unalignedByteCount = 0;
85239#ifndef MA_DR_FLAC_NO_CRC
85240 bs->crc16Cache = bs->cache >> bs->consumedBits;
85241 bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
85242#endif
85243 return MA_TRUE;
85244}
85245static void ma_dr_flac__reset_cache(ma_dr_flac_bs* bs)
85246{
85247 bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs);
85248 bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
85249 bs->cache = 0;
85250 bs->unalignedByteCount = 0;
85251 bs->unalignedCache = 0;
85252#ifndef MA_DR_FLAC_NO_CRC
85253 bs->crc16Cache = 0;
85254 bs->crc16CacheIgnoredBytes = 0;
85255#endif
85256}
85257static MA_INLINE ma_bool32 ma_dr_flac__read_uint32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint32* pResultOut)
85258{
85259 MA_DR_FLAC_ASSERT(bs != NULL);
85260 MA_DR_FLAC_ASSERT(pResultOut != NULL);
85261 MA_DR_FLAC_ASSERT(bitCount > 0);
85262 MA_DR_FLAC_ASSERT(bitCount <= 32);
85263 if (bs->consumedBits == MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) {
85264 if (!ma_dr_flac__reload_cache(bs)) {
85265 return MA_FALSE;
85266 }
85267 }
85268 if (bitCount <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
85269#ifdef MA_64BIT
85270 *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
85271 bs->consumedBits += bitCount;
85272 bs->cache <<= bitCount;
85273#else
85274 if (bitCount < MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) {
85275 *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
85276 bs->consumedBits += bitCount;
85277 bs->cache <<= bitCount;
85278 } else {
85279 *pResultOut = (ma_uint32)bs->cache;
85280 bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
85281 bs->cache = 0;
85282 }
85283#endif
85284 return MA_TRUE;
85285 } else {
85286 ma_uint32 bitCountHi = MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);
85287 ma_uint32 bitCountLo = bitCount - bitCountHi;
85288 ma_uint32 resultHi;
85289 MA_DR_FLAC_ASSERT(bitCountHi > 0);
85290 MA_DR_FLAC_ASSERT(bitCountHi < 32);
85291 resultHi = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi);
85292 if (!ma_dr_flac__reload_cache(bs)) {
85293 return MA_FALSE;
85294 }
85295 if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
85296 return MA_FALSE;
85297 }
85298 *pResultOut = (resultHi << bitCountLo) | (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo);
85299 bs->consumedBits += bitCountLo;
85300 bs->cache <<= bitCountLo;
85301 return MA_TRUE;
85302 }
85303}
85304static ma_bool32 ma_dr_flac__read_int32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int32* pResult)
85305{
85306 ma_uint32 result;
85307 MA_DR_FLAC_ASSERT(bs != NULL);
85308 MA_DR_FLAC_ASSERT(pResult != NULL);
85309 MA_DR_FLAC_ASSERT(bitCount > 0);
85310 MA_DR_FLAC_ASSERT(bitCount <= 32);
85311 if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) {
85312 return MA_FALSE;
85313 }
85314 if (bitCount < 32) {
85315 ma_uint32 signbit;
85316 signbit = ((result >> (bitCount-1)) & 0x01);
85317 result |= (~signbit + 1) << bitCount;
85318 }
85319 *pResult = (ma_int32)result;
85320 return MA_TRUE;
85321}
85322#ifdef MA_64BIT
85323static ma_bool32 ma_dr_flac__read_uint64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint64* pResultOut)
85324{
85325 ma_uint32 resultHi;
85326 ma_uint32 resultLo;
85327 MA_DR_FLAC_ASSERT(bitCount <= 64);
85328 MA_DR_FLAC_ASSERT(bitCount > 32);
85329 if (!ma_dr_flac__read_uint32(bs, bitCount - 32, &resultHi)) {
85330 return MA_FALSE;
85331 }
85332 if (!ma_dr_flac__read_uint32(bs, 32, &resultLo)) {
85333 return MA_FALSE;
85334 }
85335 *pResultOut = (((ma_uint64)resultHi) << 32) | ((ma_uint64)resultLo);
85336 return MA_TRUE;
85337}
85338#endif
85339#if 0
85340static ma_bool32 ma_dr_flac__read_int64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int64* pResultOut)
85341{
85342 ma_uint64 result;
85343 ma_uint64 signbit;
85344 MA_DR_FLAC_ASSERT(bitCount <= 64);
85345 if (!ma_dr_flac__read_uint64(bs, bitCount, &result)) {
85346 return MA_FALSE;
85347 }
85348 signbit = ((result >> (bitCount-1)) & 0x01);
85349 result |= (~signbit + 1) << bitCount;
85350 *pResultOut = (ma_int64)result;
85351 return MA_TRUE;
85352}
85353#endif
85354static ma_bool32 ma_dr_flac__read_uint16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint16* pResult)
85355{
85356 ma_uint32 result;
85357 MA_DR_FLAC_ASSERT(bs != NULL);
85358 MA_DR_FLAC_ASSERT(pResult != NULL);
85359 MA_DR_FLAC_ASSERT(bitCount > 0);
85360 MA_DR_FLAC_ASSERT(bitCount <= 16);
85361 if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) {
85362 return MA_FALSE;
85363 }
85364 *pResult = (ma_uint16)result;
85365 return MA_TRUE;
85366}
85367#if 0
85368static ma_bool32 ma_dr_flac__read_int16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int16* pResult)
85369{
85370 ma_int32 result;
85371 MA_DR_FLAC_ASSERT(bs != NULL);
85372 MA_DR_FLAC_ASSERT(pResult != NULL);
85373 MA_DR_FLAC_ASSERT(bitCount > 0);
85374 MA_DR_FLAC_ASSERT(bitCount <= 16);
85375 if (!ma_dr_flac__read_int32(bs, bitCount, &result)) {
85376 return MA_FALSE;
85377 }
85378 *pResult = (ma_int16)result;
85379 return MA_TRUE;
85380}
85381#endif
85382static ma_bool32 ma_dr_flac__read_uint8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint8* pResult)
85383{
85384 ma_uint32 result;
85385 MA_DR_FLAC_ASSERT(bs != NULL);
85386 MA_DR_FLAC_ASSERT(pResult != NULL);
85387 MA_DR_FLAC_ASSERT(bitCount > 0);
85388 MA_DR_FLAC_ASSERT(bitCount <= 8);
85389 if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) {
85390 return MA_FALSE;
85391 }
85392 *pResult = (ma_uint8)result;
85393 return MA_TRUE;
85394}
85395static ma_bool32 ma_dr_flac__read_int8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int8* pResult)
85396{
85397 ma_int32 result;
85398 MA_DR_FLAC_ASSERT(bs != NULL);
85399 MA_DR_FLAC_ASSERT(pResult != NULL);
85400 MA_DR_FLAC_ASSERT(bitCount > 0);
85401 MA_DR_FLAC_ASSERT(bitCount <= 8);
85402 if (!ma_dr_flac__read_int32(bs, bitCount, &result)) {
85403 return MA_FALSE;
85404 }
85405 *pResult = (ma_int8)result;
85406 return MA_TRUE;
85407}
85408static ma_bool32 ma_dr_flac__seek_bits(ma_dr_flac_bs* bs, size_t bitsToSeek)
85409{
85410 if (bitsToSeek <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
85411 bs->consumedBits += (ma_uint32)bitsToSeek;
85412 bs->cache <<= bitsToSeek;
85413 return MA_TRUE;
85414 } else {
85415 bitsToSeek -= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);
85416 bs->consumedBits += MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);
85417 bs->cache = 0;
85418#ifdef MA_64BIT
85419 while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) {
85420 ma_uint64 bin;
85421 if (!ma_dr_flac__read_uint64(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
85422 return MA_FALSE;
85423 }
85424 bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
85425 }
85426#else
85427 while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) {
85428 ma_uint32 bin;
85429 if (!ma_dr_flac__read_uint32(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
85430 return MA_FALSE;
85431 }
85432 bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
85433 }
85434#endif
85435 while (bitsToSeek >= 8) {
85436 ma_uint8 bin;
85437 if (!ma_dr_flac__read_uint8(bs, 8, &bin)) {
85438 return MA_FALSE;
85439 }
85440 bitsToSeek -= 8;
85441 }
85442 if (bitsToSeek > 0) {
85443 ma_uint8 bin;
85444 if (!ma_dr_flac__read_uint8(bs, (ma_uint32)bitsToSeek, &bin)) {
85445 return MA_FALSE;
85446 }
85447 bitsToSeek = 0;
85448 }
85449 MA_DR_FLAC_ASSERT(bitsToSeek == 0);
85450 return MA_TRUE;
85451 }
85452}
85453static ma_bool32 ma_dr_flac__find_and_seek_to_next_sync_code(ma_dr_flac_bs* bs)
85454{
85455 MA_DR_FLAC_ASSERT(bs != NULL);
85456 if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
85457 return MA_FALSE;
85458 }
85459 for (;;) {
85460 ma_uint8 hi;
85461#ifndef MA_DR_FLAC_NO_CRC
85462 ma_dr_flac__reset_crc16(bs);
85463#endif
85464 if (!ma_dr_flac__read_uint8(bs, 8, &hi)) {
85465 return MA_FALSE;
85466 }
85467 if (hi == 0xFF) {
85468 ma_uint8 lo;
85469 if (!ma_dr_flac__read_uint8(bs, 6, &lo)) {
85470 return MA_FALSE;
85471 }
85472 if (lo == 0x3E) {
85473 return MA_TRUE;
85474 } else {
85475 if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
85476 return MA_FALSE;
85477 }
85478 }
85479 }
85480 }
85481}
85482#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC)
85483#define MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT
85484#endif
85485#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(MA_X64) || defined(MA_X86)) && !defined(__clang__)
85486#define MA_DR_FLAC_IMPLEMENT_CLZ_MSVC
85487#endif
85488#if defined(__WATCOMC__) && defined(__386__)
85489#define MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM
85490#endif
85491#ifdef __MRC__
85492#include <intrinsics.h>
85493#define MA_DR_FLAC_IMPLEMENT_CLZ_MRC
85494#endif
85495static MA_INLINE ma_uint32 ma_dr_flac__clz_software(ma_dr_flac_cache_t x)
85496{
85497 ma_uint32 n;
85498 static ma_uint32 clz_table_4[] = {
85499 0,
85500 4,
85501 3, 3,
85502 2, 2, 2, 2,
85503 1, 1, 1, 1, 1, 1, 1, 1
85504 };
85505 if (x == 0) {
85506 return sizeof(x)*8;
85507 }
85508 n = clz_table_4[x >> (sizeof(x)*8 - 4)];
85509 if (n == 0) {
85510#ifdef MA_64BIT
85511 if ((x & ((ma_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; }
85512 if ((x & ((ma_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; }
85513 if ((x & ((ma_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; }
85514 if ((x & ((ma_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; }
85515#else
85516 if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; }
85517 if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; }
85518 if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; }
85519#endif
85520 n += clz_table_4[x >> (sizeof(x)*8 - 4)];
85521 }
85522 return n - 1;
85523}
85524#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT
85525static MA_INLINE ma_bool32 ma_dr_flac__is_lzcnt_supported(void)
85526{
85527#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
85528 return MA_TRUE;
85529#elif defined(__MRC__)
85530 return MA_TRUE;
85531#else
85532 #ifdef MA_DR_FLAC_HAS_LZCNT_INTRINSIC
85533 return ma_dr_flac__gIsLZCNTSupported;
85534 #else
85535 return MA_FALSE;
85536 #endif
85537#endif
85538}
85539static MA_INLINE ma_uint32 ma_dr_flac__clz_lzcnt(ma_dr_flac_cache_t x)
85540{
85541#if defined(_MSC_VER)
85542 #ifdef MA_64BIT
85543 return (ma_uint32)__lzcnt64(x);
85544 #else
85545 return (ma_uint32)__lzcnt(x);
85546 #endif
85547#else
85548 #if defined(__GNUC__) || defined(__clang__)
85549 #if defined(MA_X64)
85550 {
85551 ma_uint64 r;
85552 __asm__ __volatile__ (
85553 "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
85554 );
85555 return (ma_uint32)r;
85556 }
85557 #elif defined(MA_X86)
85558 {
85559 ma_uint32 r;
85560 __asm__ __volatile__ (
85561 "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
85562 );
85563 return r;
85564 }
85565 #elif defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT)
85566 {
85567 unsigned int r;
85568 __asm__ __volatile__ (
85569 #if defined(MA_64BIT)
85570 "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x)
85571 #else
85572 "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x)
85573 #endif
85574 );
85575 return r;
85576 }
85577 #else
85578 if (x == 0) {
85579 return sizeof(x)*8;
85580 }
85581 #ifdef MA_64BIT
85582 return (ma_uint32)__builtin_clzll((ma_uint64)x);
85583 #else
85584 return (ma_uint32)__builtin_clzl((ma_uint32)x);
85585 #endif
85586 #endif
85587 #else
85588 #error "This compiler does not support the lzcnt intrinsic."
85589 #endif
85590#endif
85591}
85592#endif
85593#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC
85594#include <intrin.h>
85595static MA_INLINE ma_uint32 ma_dr_flac__clz_msvc(ma_dr_flac_cache_t x)
85596{
85597 ma_uint32 n;
85598 if (x == 0) {
85599 return sizeof(x)*8;
85600 }
85601#ifdef MA_64BIT
85602 _BitScanReverse64((unsigned long*)&n, x);
85603#else
85604 _BitScanReverse((unsigned long*)&n, x);
85605#endif
85606 return sizeof(x)*8 - n - 1;
85607}
85608#endif
85609#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM
85610static __inline ma_uint32 ma_dr_flac__clz_watcom (ma_uint32);
85611#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT
85612#pragma aux ma_dr_flac__clz_watcom_lzcnt = \
85613 "db 0F3h, 0Fh, 0BDh, 0C0h" \
85614 parm [eax] \
85615 value [eax] \
85616 modify nomemory;
85617#else
85618#pragma aux ma_dr_flac__clz_watcom = \
85619 "bsr eax, eax" \
85620 "xor eax, 31" \
85621 parm [eax] nomemory \
85622 value [eax] \
85623 modify exact [eax] nomemory;
85624#endif
85625#endif
85626static MA_INLINE ma_uint32 ma_dr_flac__clz(ma_dr_flac_cache_t x)
85627{
85628#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT
85629 if (ma_dr_flac__is_lzcnt_supported()) {
85630 return ma_dr_flac__clz_lzcnt(x);
85631 } else
85632#endif
85633 {
85634#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC
85635 return ma_dr_flac__clz_msvc(x);
85636#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT)
85637 return ma_dr_flac__clz_watcom_lzcnt(x);
85638#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM)
85639 return (x == 0) ? sizeof(x)*8 : ma_dr_flac__clz_watcom(x);
85640#elif defined(__MRC__)
85641 return __cntlzw(x);
85642#else
85643 return ma_dr_flac__clz_software(x);
85644#endif
85645 }
85646}
85647static MA_INLINE ma_bool32 ma_dr_flac__seek_past_next_set_bit(ma_dr_flac_bs* bs, unsigned int* pOffsetOut)
85648{
85649 ma_uint32 zeroCounter = 0;
85650 ma_uint32 setBitOffsetPlus1;
85651 while (bs->cache == 0) {
85652 zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);
85653 if (!ma_dr_flac__reload_cache(bs)) {
85654 return MA_FALSE;
85655 }
85656 }
85657 if (bs->cache == 1) {
85658 *pOffsetOut = zeroCounter + (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) - 1;
85659 if (!ma_dr_flac__reload_cache(bs)) {
85660 return MA_FALSE;
85661 }
85662 return MA_TRUE;
85663 }
85664 setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache);
85665 setBitOffsetPlus1 += 1;
85666 if (setBitOffsetPlus1 > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
85667 return MA_FALSE;
85668 }
85669 bs->consumedBits += setBitOffsetPlus1;
85670 bs->cache <<= setBitOffsetPlus1;
85671 *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1;
85672 return MA_TRUE;
85673}
85674static ma_bool32 ma_dr_flac__seek_to_byte(ma_dr_flac_bs* bs, ma_uint64 offsetFromStart)
85675{
85676 MA_DR_FLAC_ASSERT(bs != NULL);
85677 MA_DR_FLAC_ASSERT(offsetFromStart > 0);
85678 if (offsetFromStart > 0x7FFFFFFF) {
85679 ma_uint64 bytesRemaining = offsetFromStart;
85680 if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, MA_DR_FLAC_SEEK_SET)) {
85681 return MA_FALSE;
85682 }
85683 bytesRemaining -= 0x7FFFFFFF;
85684 while (bytesRemaining > 0x7FFFFFFF) {
85685 if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, MA_DR_FLAC_SEEK_CUR)) {
85686 return MA_FALSE;
85687 }
85688 bytesRemaining -= 0x7FFFFFFF;
85689 }
85690 if (bytesRemaining > 0) {
85691 if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, MA_DR_FLAC_SEEK_CUR)) {
85692 return MA_FALSE;
85693 }
85694 }
85695 } else {
85696 if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, MA_DR_FLAC_SEEK_SET)) {
85697 return MA_FALSE;
85698 }
85699 }
85700 ma_dr_flac__reset_cache(bs);
85701 return MA_TRUE;
85702}
85703static ma_result ma_dr_flac__read_utf8_coded_number(ma_dr_flac_bs* bs, ma_uint64* pNumberOut, ma_uint8* pCRCOut)
85704{
85705 ma_uint8 crc;
85706 ma_uint64 result;
85707 ma_uint8 utf8[7] = {0};
85708 int byteCount;
85709 int i;
85710 MA_DR_FLAC_ASSERT(bs != NULL);
85711 MA_DR_FLAC_ASSERT(pNumberOut != NULL);
85712 MA_DR_FLAC_ASSERT(pCRCOut != NULL);
85713 crc = *pCRCOut;
85714 if (!ma_dr_flac__read_uint8(bs, 8, utf8)) {
85715 *pNumberOut = 0;
85716 return MA_AT_END;
85717 }
85718 crc = ma_dr_flac_crc8(crc, utf8[0], 8);
85719 if ((utf8[0] & 0x80) == 0) {
85720 *pNumberOut = utf8[0];
85721 *pCRCOut = crc;
85722 return MA_SUCCESS;
85723 }
85724 if ((utf8[0] & 0xE0) == 0xC0) {
85725 byteCount = 2;
85726 } else if ((utf8[0] & 0xF0) == 0xE0) {
85727 byteCount = 3;
85728 } else if ((utf8[0] & 0xF8) == 0xF0) {
85729 byteCount = 4;
85730 } else if ((utf8[0] & 0xFC) == 0xF8) {
85731 byteCount = 5;
85732 } else if ((utf8[0] & 0xFE) == 0xFC) {
85733 byteCount = 6;
85734 } else if ((utf8[0] & 0xFF) == 0xFE) {
85735 byteCount = 7;
85736 } else {
85737 *pNumberOut = 0;
85738 return MA_CRC_MISMATCH;
85739 }
85740 MA_DR_FLAC_ASSERT(byteCount > 1);
85741 result = (ma_uint64)(utf8[0] & (0xFF >> (byteCount + 1)));
85742 for (i = 1; i < byteCount; ++i) {
85743 if (!ma_dr_flac__read_uint8(bs, 8, utf8 + i)) {
85744 *pNumberOut = 0;
85745 return MA_AT_END;
85746 }
85747 crc = ma_dr_flac_crc8(crc, utf8[i], 8);
85748 result = (result << 6) | (utf8[i] & 0x3F);
85749 }
85750 *pNumberOut = result;
85751 *pCRCOut = crc;
85752 return MA_SUCCESS;
85753}
85754static MA_INLINE ma_uint32 ma_dr_flac__ilog2_u32(ma_uint32 x)
85755{
85756#if 1
85757 ma_uint32 result = 0;
85758 while (x > 0) {
85759 result += 1;
85760 x >>= 1;
85761 }
85762 return result;
85763#endif
85764}
85765static MA_INLINE ma_bool32 ma_dr_flac__use_64_bit_prediction(ma_uint32 bitsPerSample, ma_uint32 order, ma_uint32 precision)
85766{
85767 return bitsPerSample + precision + ma_dr_flac__ilog2_u32(order) > 32;
85768}
85769#if defined(__clang__)
85770__attribute__((no_sanitize("signed-integer-overflow")))
85771#endif
85772static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_32(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples)
85773{
85774 ma_int32 prediction = 0;
85775 MA_DR_FLAC_ASSERT(order <= 32);
85776 switch (order)
85777 {
85778 case 32: prediction += coefficients[31] * pDecodedSamples[-32];
85779 case 31: prediction += coefficients[30] * pDecodedSamples[-31];
85780 case 30: prediction += coefficients[29] * pDecodedSamples[-30];
85781 case 29: prediction += coefficients[28] * pDecodedSamples[-29];
85782 case 28: prediction += coefficients[27] * pDecodedSamples[-28];
85783 case 27: prediction += coefficients[26] * pDecodedSamples[-27];
85784 case 26: prediction += coefficients[25] * pDecodedSamples[-26];
85785 case 25: prediction += coefficients[24] * pDecodedSamples[-25];
85786 case 24: prediction += coefficients[23] * pDecodedSamples[-24];
85787 case 23: prediction += coefficients[22] * pDecodedSamples[-23];
85788 case 22: prediction += coefficients[21] * pDecodedSamples[-22];
85789 case 21: prediction += coefficients[20] * pDecodedSamples[-21];
85790 case 20: prediction += coefficients[19] * pDecodedSamples[-20];
85791 case 19: prediction += coefficients[18] * pDecodedSamples[-19];
85792 case 18: prediction += coefficients[17] * pDecodedSamples[-18];
85793 case 17: prediction += coefficients[16] * pDecodedSamples[-17];
85794 case 16: prediction += coefficients[15] * pDecodedSamples[-16];
85795 case 15: prediction += coefficients[14] * pDecodedSamples[-15];
85796 case 14: prediction += coefficients[13] * pDecodedSamples[-14];
85797 case 13: prediction += coefficients[12] * pDecodedSamples[-13];
85798 case 12: prediction += coefficients[11] * pDecodedSamples[-12];
85799 case 11: prediction += coefficients[10] * pDecodedSamples[-11];
85800 case 10: prediction += coefficients[ 9] * pDecodedSamples[-10];
85801 case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9];
85802 case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8];
85803 case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7];
85804 case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6];
85805 case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5];
85806 case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4];
85807 case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3];
85808 case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2];
85809 case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1];
85810 }
85811 return (ma_int32)(prediction >> shift);
85812}
85813static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_64(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples)
85814{
85815 ma_int64 prediction;
85816 MA_DR_FLAC_ASSERT(order <= 32);
85817#ifndef MA_64BIT
85818 if (order == 8)
85819 {
85820 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
85821 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
85822 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
85823 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
85824 prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
85825 prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
85826 prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
85827 prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8];
85828 }
85829 else if (order == 7)
85830 {
85831 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
85832 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
85833 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
85834 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
85835 prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
85836 prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
85837 prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
85838 }
85839 else if (order == 3)
85840 {
85841 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
85842 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
85843 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
85844 }
85845 else if (order == 6)
85846 {
85847 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
85848 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
85849 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
85850 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
85851 prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
85852 prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
85853 }
85854 else if (order == 5)
85855 {
85856 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
85857 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
85858 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
85859 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
85860 prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
85861 }
85862 else if (order == 4)
85863 {
85864 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
85865 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
85866 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
85867 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
85868 }
85869 else if (order == 12)
85870 {
85871 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
85872 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
85873 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
85874 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
85875 prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
85876 prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
85877 prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
85878 prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8];
85879 prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9];
85880 prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10];
85881 prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11];
85882 prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12];
85883 }
85884 else if (order == 2)
85885 {
85886 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
85887 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
85888 }
85889 else if (order == 1)
85890 {
85891 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
85892 }
85893 else if (order == 10)
85894 {
85895 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
85896 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
85897 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
85898 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
85899 prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
85900 prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
85901 prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
85902 prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8];
85903 prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9];
85904 prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10];
85905 }
85906 else if (order == 9)
85907 {
85908 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
85909 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
85910 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
85911 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
85912 prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
85913 prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
85914 prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
85915 prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8];
85916 prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9];
85917 }
85918 else if (order == 11)
85919 {
85920 prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
85921 prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
85922 prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
85923 prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
85924 prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
85925 prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
85926 prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
85927 prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8];
85928 prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9];
85929 prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10];
85930 prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11];
85931 }
85932 else
85933 {
85934 int j;
85935 prediction = 0;
85936 for (j = 0; j < (int)order; ++j) {
85937 prediction += coefficients[j] * (ma_int64)pDecodedSamples[-j-1];
85938 }
85939 }
85940#endif
85941#ifdef MA_64BIT
85942 prediction = 0;
85943 switch (order)
85944 {
85945 case 32: prediction += coefficients[31] * (ma_int64)pDecodedSamples[-32];
85946 case 31: prediction += coefficients[30] * (ma_int64)pDecodedSamples[-31];
85947 case 30: prediction += coefficients[29] * (ma_int64)pDecodedSamples[-30];
85948 case 29: prediction += coefficients[28] * (ma_int64)pDecodedSamples[-29];
85949 case 28: prediction += coefficients[27] * (ma_int64)pDecodedSamples[-28];
85950 case 27: prediction += coefficients[26] * (ma_int64)pDecodedSamples[-27];
85951 case 26: prediction += coefficients[25] * (ma_int64)pDecodedSamples[-26];
85952 case 25: prediction += coefficients[24] * (ma_int64)pDecodedSamples[-25];
85953 case 24: prediction += coefficients[23] * (ma_int64)pDecodedSamples[-24];
85954 case 23: prediction += coefficients[22] * (ma_int64)pDecodedSamples[-23];
85955 case 22: prediction += coefficients[21] * (ma_int64)pDecodedSamples[-22];
85956 case 21: prediction += coefficients[20] * (ma_int64)pDecodedSamples[-21];
85957 case 20: prediction += coefficients[19] * (ma_int64)pDecodedSamples[-20];
85958 case 19: prediction += coefficients[18] * (ma_int64)pDecodedSamples[-19];
85959 case 18: prediction += coefficients[17] * (ma_int64)pDecodedSamples[-18];
85960 case 17: prediction += coefficients[16] * (ma_int64)pDecodedSamples[-17];
85961 case 16: prediction += coefficients[15] * (ma_int64)pDecodedSamples[-16];
85962 case 15: prediction += coefficients[14] * (ma_int64)pDecodedSamples[-15];
85963 case 14: prediction += coefficients[13] * (ma_int64)pDecodedSamples[-14];
85964 case 13: prediction += coefficients[12] * (ma_int64)pDecodedSamples[-13];
85965 case 12: prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12];
85966 case 11: prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11];
85967 case 10: prediction += coefficients[ 9] * (ma_int64)pDecodedSamples[-10];
85968 case 9: prediction += coefficients[ 8] * (ma_int64)pDecodedSamples[- 9];
85969 case 8: prediction += coefficients[ 7] * (ma_int64)pDecodedSamples[- 8];
85970 case 7: prediction += coefficients[ 6] * (ma_int64)pDecodedSamples[- 7];
85971 case 6: prediction += coefficients[ 5] * (ma_int64)pDecodedSamples[- 6];
85972 case 5: prediction += coefficients[ 4] * (ma_int64)pDecodedSamples[- 5];
85973 case 4: prediction += coefficients[ 3] * (ma_int64)pDecodedSamples[- 4];
85974 case 3: prediction += coefficients[ 2] * (ma_int64)pDecodedSamples[- 3];
85975 case 2: prediction += coefficients[ 1] * (ma_int64)pDecodedSamples[- 2];
85976 case 1: prediction += coefficients[ 0] * (ma_int64)pDecodedSamples[- 1];
85977 }
85978#endif
85979 return (ma_int32)(prediction >> shift);
85980}
85981#if 0
85982static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__reference(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)
85983{
85984 ma_uint32 i;
85985 MA_DR_FLAC_ASSERT(bs != NULL);
85986 MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
85987 for (i = 0; i < count; ++i) {
85988 ma_uint32 zeroCounter = 0;
85989 for (;;) {
85990 ma_uint8 bit;
85991 if (!ma_dr_flac__read_uint8(bs, 1, &bit)) {
85992 return MA_FALSE;
85993 }
85994 if (bit == 0) {
85995 zeroCounter += 1;
85996 } else {
85997 break;
85998 }
85999 }
86000 ma_uint32 decodedRice;
86001 if (riceParam > 0) {
86002 if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) {
86003 return MA_FALSE;
86004 }
86005 } else {
86006 decodedRice = 0;
86007 }
86008 decodedRice |= (zeroCounter << riceParam);
86009 if ((decodedRice & 0x01)) {
86010 decodedRice = ~(decodedRice >> 1);
86011 } else {
86012 decodedRice = (decodedRice >> 1);
86013 }
86014 if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
86015 pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
86016 } else {
86017 pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
86018 }
86019 }
86020 return MA_TRUE;
86021}
86022#endif
86023#if 0
86024static ma_bool32 ma_dr_flac__read_rice_parts__reference(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut)
86025{
86026 ma_uint32 zeroCounter = 0;
86027 ma_uint32 decodedRice;
86028 for (;;) {
86029 ma_uint8 bit;
86030 if (!ma_dr_flac__read_uint8(bs, 1, &bit)) {
86031 return MA_FALSE;
86032 }
86033 if (bit == 0) {
86034 zeroCounter += 1;
86035 } else {
86036 break;
86037 }
86038 }
86039 if (riceParam > 0) {
86040 if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) {
86041 return MA_FALSE;
86042 }
86043 } else {
86044 decodedRice = 0;
86045 }
86046 *pZeroCounterOut = zeroCounter;
86047 *pRiceParamPartOut = decodedRice;
86048 return MA_TRUE;
86049}
86050#endif
86051#if 0
86052static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut)
86053{
86054 ma_dr_flac_cache_t riceParamMask;
86055 ma_uint32 zeroCounter;
86056 ma_uint32 setBitOffsetPlus1;
86057 ma_uint32 riceParamPart;
86058 ma_uint32 riceLength;
86059 MA_DR_FLAC_ASSERT(riceParam > 0);
86060 riceParamMask = MA_DR_FLAC_CACHE_L1_SELECTION_MASK(riceParam);
86061 zeroCounter = 0;
86062 while (bs->cache == 0) {
86063 zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);
86064 if (!ma_dr_flac__reload_cache(bs)) {
86065 return MA_FALSE;
86066 }
86067 }
86068 setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache);
86069 zeroCounter += setBitOffsetPlus1;
86070 setBitOffsetPlus1 += 1;
86071 riceLength = setBitOffsetPlus1 + riceParam;
86072 if (riceLength < MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
86073 riceParamPart = (ma_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength));
86074 bs->consumedBits += riceLength;
86075 bs->cache <<= riceLength;
86076 } else {
86077 ma_uint32 bitCountLo;
86078 ma_dr_flac_cache_t resultHi;
86079 bs->consumedBits += riceLength;
86080 bs->cache <<= setBitOffsetPlus1 & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1);
86081 bitCountLo = bs->consumedBits - MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
86082 resultHi = MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam);
86083 if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
86084#ifndef MA_DR_FLAC_NO_CRC
86085 ma_dr_flac__update_crc16(bs);
86086#endif
86087 bs->cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
86088 bs->consumedBits = 0;
86089#ifndef MA_DR_FLAC_NO_CRC
86090 bs->crc16Cache = bs->cache;
86091#endif
86092 } else {
86093 if (!ma_dr_flac__reload_cache(bs)) {
86094 return MA_FALSE;
86095 }
86096 if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
86097 return MA_FALSE;
86098 }
86099 }
86100 riceParamPart = (ma_uint32)(resultHi | MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo));
86101 bs->consumedBits += bitCountLo;
86102 bs->cache <<= bitCountLo;
86103 }
86104 pZeroCounterOut[0] = zeroCounter;
86105 pRiceParamPartOut[0] = riceParamPart;
86106 return MA_TRUE;
86107}
86108#endif
86109static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts_x1(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut)
86110{
86111 ma_uint32 riceParamPlus1 = riceParam + 1;
86112 ma_uint32 riceParamPlus1Shift = MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1);
86113 ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
86114 ma_dr_flac_cache_t bs_cache = bs->cache;
86115 ma_uint32 bs_consumedBits = bs->consumedBits;
86116 ma_uint32 lzcount = ma_dr_flac__clz(bs_cache);
86117 if (lzcount < sizeof(bs_cache)*8) {
86118 pZeroCounterOut[0] = lzcount;
86119 extract_rice_param_part:
86120 bs_cache <<= lzcount;
86121 bs_consumedBits += lzcount;
86122 if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
86123 pRiceParamPartOut[0] = (ma_uint32)(bs_cache >> riceParamPlus1Shift);
86124 bs_cache <<= riceParamPlus1;
86125 bs_consumedBits += riceParamPlus1;
86126 } else {
86127 ma_uint32 riceParamPartHi;
86128 ma_uint32 riceParamPartLo;
86129 ma_uint32 riceParamPartLoBitCount;
86130 riceParamPartHi = (ma_uint32)(bs_cache >> riceParamPlus1Shift);
86131 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
86132 MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
86133 if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
86134 #ifndef MA_DR_FLAC_NO_CRC
86135 ma_dr_flac__update_crc16(bs);
86136 #endif
86137 bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
86138 bs_consumedBits = riceParamPartLoBitCount;
86139 #ifndef MA_DR_FLAC_NO_CRC
86140 bs->crc16Cache = bs_cache;
86141 #endif
86142 } else {
86143 if (!ma_dr_flac__reload_cache(bs)) {
86144 return MA_FALSE;
86145 }
86146 if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
86147 return MA_FALSE;
86148 }
86149 bs_cache = bs->cache;
86150 bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
86151 }
86152 riceParamPartLo = (ma_uint32)(bs_cache >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount)));
86153 pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo;
86154 bs_cache <<= riceParamPartLoBitCount;
86155 }
86156 } else {
86157 ma_uint32 zeroCounter = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits);
86158 for (;;) {
86159 if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
86160 #ifndef MA_DR_FLAC_NO_CRC
86161 ma_dr_flac__update_crc16(bs);
86162 #endif
86163 bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
86164 bs_consumedBits = 0;
86165 #ifndef MA_DR_FLAC_NO_CRC
86166 bs->crc16Cache = bs_cache;
86167 #endif
86168 } else {
86169 if (!ma_dr_flac__reload_cache(bs)) {
86170 return MA_FALSE;
86171 }
86172 bs_cache = bs->cache;
86173 bs_consumedBits = bs->consumedBits;
86174 }
86175 lzcount = ma_dr_flac__clz(bs_cache);
86176 zeroCounter += lzcount;
86177 if (lzcount < sizeof(bs_cache)*8) {
86178 break;
86179 }
86180 }
86181 pZeroCounterOut[0] = zeroCounter;
86182 goto extract_rice_param_part;
86183 }
86184 bs->cache = bs_cache;
86185 bs->consumedBits = bs_consumedBits;
86186 return MA_TRUE;
86187}
86188static MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam)
86189{
86190 ma_uint32 riceParamPlus1 = riceParam + 1;
86191 ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
86192 ma_dr_flac_cache_t bs_cache = bs->cache;
86193 ma_uint32 bs_consumedBits = bs->consumedBits;
86194 ma_uint32 lzcount = ma_dr_flac__clz(bs_cache);
86195 if (lzcount < sizeof(bs_cache)*8) {
86196 extract_rice_param_part:
86197 bs_cache <<= lzcount;
86198 bs_consumedBits += lzcount;
86199 if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
86200 bs_cache <<= riceParamPlus1;
86201 bs_consumedBits += riceParamPlus1;
86202 } else {
86203 ma_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
86204 MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
86205 if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
86206 #ifndef MA_DR_FLAC_NO_CRC
86207 ma_dr_flac__update_crc16(bs);
86208 #endif
86209 bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
86210 bs_consumedBits = riceParamPartLoBitCount;
86211 #ifndef MA_DR_FLAC_NO_CRC
86212 bs->crc16Cache = bs_cache;
86213 #endif
86214 } else {
86215 if (!ma_dr_flac__reload_cache(bs)) {
86216 return MA_FALSE;
86217 }
86218 if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
86219 return MA_FALSE;
86220 }
86221 bs_cache = bs->cache;
86222 bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
86223 }
86224 bs_cache <<= riceParamPartLoBitCount;
86225 }
86226 } else {
86227 for (;;) {
86228 if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
86229 #ifndef MA_DR_FLAC_NO_CRC
86230 ma_dr_flac__update_crc16(bs);
86231 #endif
86232 bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
86233 bs_consumedBits = 0;
86234 #ifndef MA_DR_FLAC_NO_CRC
86235 bs->crc16Cache = bs_cache;
86236 #endif
86237 } else {
86238 if (!ma_dr_flac__reload_cache(bs)) {
86239 return MA_FALSE;
86240 }
86241 bs_cache = bs->cache;
86242 bs_consumedBits = bs->consumedBits;
86243 }
86244 lzcount = ma_dr_flac__clz(bs_cache);
86245 if (lzcount < sizeof(bs_cache)*8) {
86246 break;
86247 }
86248 }
86249 goto extract_rice_param_part;
86250 }
86251 bs->cache = bs_cache;
86252 bs->consumedBits = bs_consumedBits;
86253 return MA_TRUE;
86254}
86255static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut)
86256{
86257 ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
86258 ma_uint32 zeroCountPart0;
86259 ma_uint32 riceParamPart0;
86260 ma_uint32 riceParamMask;
86261 ma_uint32 i;
86262 MA_DR_FLAC_ASSERT(bs != NULL);
86263 MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
86264 (void)bitsPerSample;
86265 (void)order;
86266 (void)shift;
86267 (void)coefficients;
86268 riceParamMask = (ma_uint32)~((~0UL) << riceParam);
86269 i = 0;
86270 while (i < count) {
86271 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
86272 return MA_FALSE;
86273 }
86274 riceParamPart0 &= riceParamMask;
86275 riceParamPart0 |= (zeroCountPart0 << riceParam);
86276 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
86277 pSamplesOut[i] = riceParamPart0;
86278 i += 1;
86279 }
86280 return MA_TRUE;
86281}
86282static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)
86283{
86284 ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
86285 ma_uint32 zeroCountPart0 = 0;
86286 ma_uint32 zeroCountPart1 = 0;
86287 ma_uint32 zeroCountPart2 = 0;
86288 ma_uint32 zeroCountPart3 = 0;
86289 ma_uint32 riceParamPart0 = 0;
86290 ma_uint32 riceParamPart1 = 0;
86291 ma_uint32 riceParamPart2 = 0;
86292 ma_uint32 riceParamPart3 = 0;
86293 ma_uint32 riceParamMask;
86294 const ma_int32* pSamplesOutEnd;
86295 ma_uint32 i;
86296 MA_DR_FLAC_ASSERT(bs != NULL);
86297 MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
86298 if (lpcOrder == 0) {
86299 return ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
86300 }
86301 riceParamMask = (ma_uint32)~((~0UL) << riceParam);
86302 pSamplesOutEnd = pSamplesOut + (count & ~3);
86303 if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
86304 while (pSamplesOut < pSamplesOutEnd) {
86305 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
86306 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
86307 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
86308 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
86309 return MA_FALSE;
86310 }
86311 riceParamPart0 &= riceParamMask;
86312 riceParamPart1 &= riceParamMask;
86313 riceParamPart2 &= riceParamMask;
86314 riceParamPart3 &= riceParamMask;
86315 riceParamPart0 |= (zeroCountPart0 << riceParam);
86316 riceParamPart1 |= (zeroCountPart1 << riceParam);
86317 riceParamPart2 |= (zeroCountPart2 << riceParam);
86318 riceParamPart3 |= (zeroCountPart3 << riceParam);
86319 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
86320 riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
86321 riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
86322 riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
86323 pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
86324 pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1);
86325 pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2);
86326 pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3);
86327 pSamplesOut += 4;
86328 }
86329 } else {
86330 while (pSamplesOut < pSamplesOutEnd) {
86331 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
86332 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
86333 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
86334 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
86335 return MA_FALSE;
86336 }
86337 riceParamPart0 &= riceParamMask;
86338 riceParamPart1 &= riceParamMask;
86339 riceParamPart2 &= riceParamMask;
86340 riceParamPart3 &= riceParamMask;
86341 riceParamPart0 |= (zeroCountPart0 << riceParam);
86342 riceParamPart1 |= (zeroCountPart1 << riceParam);
86343 riceParamPart2 |= (zeroCountPart2 << riceParam);
86344 riceParamPart3 |= (zeroCountPart3 << riceParam);
86345 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
86346 riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
86347 riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
86348 riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
86349 pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
86350 pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1);
86351 pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2);
86352 pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3);
86353 pSamplesOut += 4;
86354 }
86355 }
86356 i = (count & ~3);
86357 while (i < count) {
86358 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
86359 return MA_FALSE;
86360 }
86361 riceParamPart0 &= riceParamMask;
86362 riceParamPart0 |= (zeroCountPart0 << riceParam);
86363 riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
86364 if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
86365 pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
86366 } else {
86367 pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
86368 }
86369 i += 1;
86370 pSamplesOut += 1;
86371 }
86372 return MA_TRUE;
86373}
86374#if defined(MA_DR_FLAC_SUPPORT_SSE2)
86375static MA_INLINE __m128i ma_dr_flac__mm_packs_interleaved_epi32(__m128i a, __m128i b)
86376{
86377 __m128i r;
86378 r = _mm_packs_epi32(a, b);
86379 r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0));
86380 r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));
86381 r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));
86382 return r;
86383}
86384#endif
86385#if defined(MA_DR_FLAC_SUPPORT_SSE41)
86386static MA_INLINE __m128i ma_dr_flac__mm_not_si128(__m128i a)
86387{
86388 return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128()));
86389}
86390static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi32(__m128i x)
86391{
86392 __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));
86393 __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2));
86394 return _mm_add_epi32(x64, x32);
86395}
86396static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi64(__m128i x)
86397{
86398 return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));
86399}
86400static MA_INLINE __m128i ma_dr_flac__mm_srai_epi64(__m128i x, int count)
86401{
86402 __m128i lo = _mm_srli_epi64(x, count);
86403 __m128i hi = _mm_srai_epi32(x, count);
86404 hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0));
86405 return _mm_or_si128(lo, hi);
86406}
86407static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut)
86408{
86409 int i;
86410 ma_uint32 riceParamMask;
86411 ma_int32* pDecodedSamples = pSamplesOut;
86412 ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
86413 ma_uint32 zeroCountParts0 = 0;
86414 ma_uint32 zeroCountParts1 = 0;
86415 ma_uint32 zeroCountParts2 = 0;
86416 ma_uint32 zeroCountParts3 = 0;
86417 ma_uint32 riceParamParts0 = 0;
86418 ma_uint32 riceParamParts1 = 0;
86419 ma_uint32 riceParamParts2 = 0;
86420 ma_uint32 riceParamParts3 = 0;
86421 __m128i coefficients128_0;
86422 __m128i coefficients128_4;
86423 __m128i coefficients128_8;
86424 __m128i samples128_0;
86425 __m128i samples128_4;
86426 __m128i samples128_8;
86427 __m128i riceParamMask128;
86428 const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
86429 riceParamMask = (ma_uint32)~((~0UL) << riceParam);
86430 riceParamMask128 = _mm_set1_epi32(riceParamMask);
86431 coefficients128_0 = _mm_setzero_si128();
86432 coefficients128_4 = _mm_setzero_si128();
86433 coefficients128_8 = _mm_setzero_si128();
86434 samples128_0 = _mm_setzero_si128();
86435 samples128_4 = _mm_setzero_si128();
86436 samples128_8 = _mm_setzero_si128();
86437#if 1
86438 {
86439 int runningOrder = order;
86440 if (runningOrder >= 4) {
86441 coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));
86442 samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4));
86443 runningOrder -= 4;
86444 } else {
86445 switch (runningOrder) {
86446 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;
86447 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;
86448 case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break;
86449 }
86450 runningOrder = 0;
86451 }
86452 if (runningOrder >= 4) {
86453 coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));
86454 samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8));
86455 runningOrder -= 4;
86456 } else {
86457 switch (runningOrder) {
86458 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;
86459 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;
86460 case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break;
86461 }
86462 runningOrder = 0;
86463 }
86464 if (runningOrder == 4) {
86465 coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));
86466 samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12));
86467 runningOrder -= 4;
86468 } else {
86469 switch (runningOrder) {
86470 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;
86471 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;
86472 case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break;
86473 }
86474 runningOrder = 0;
86475 }
86476 coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));
86477 coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));
86478 coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));
86479 }
86480#else
86481 switch (order)
86482 {
86483 case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12];
86484 case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11];
86485 case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10];
86486 case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9];
86487 case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8];
86488 case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7];
86489 case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6];
86490 case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5];
86491 case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4];
86492 case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3];
86493 case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2];
86494 case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1];
86495 }
86496#endif
86497 while (pDecodedSamples < pDecodedSamplesEnd) {
86498 __m128i prediction128;
86499 __m128i zeroCountPart128;
86500 __m128i riceParamPart128;
86501 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
86502 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
86503 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
86504 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
86505 return MA_FALSE;
86506 }
86507 zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
86508 riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
86509 riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
86510 riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
86511 riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01)));
86512 if (order <= 4) {
86513 for (i = 0; i < 4; i += 1) {
86514 prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0);
86515 prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128);
86516 prediction128 = _mm_srai_epi32(prediction128, shift);
86517 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
86518 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
86519 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
86520 }
86521 } else if (order <= 8) {
86522 for (i = 0; i < 4; i += 1) {
86523 prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4);
86524 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));
86525 prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128);
86526 prediction128 = _mm_srai_epi32(prediction128, shift);
86527 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
86528 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
86529 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
86530 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
86531 }
86532 } else {
86533 for (i = 0; i < 4; i += 1) {
86534 prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8);
86535 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4));
86536 prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));
86537 prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128);
86538 prediction128 = _mm_srai_epi32(prediction128, shift);
86539 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
86540 samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4);
86541 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
86542 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
86543 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
86544 }
86545 }
86546 _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);
86547 pDecodedSamples += 4;
86548 }
86549 i = (count & ~3);
86550 while (i < (int)count) {
86551 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
86552 return MA_FALSE;
86553 }
86554 riceParamParts0 &= riceParamMask;
86555 riceParamParts0 |= (zeroCountParts0 << riceParam);
86556 riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
86557 pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);
86558 i += 1;
86559 pDecodedSamples += 1;
86560 }
86561 return MA_TRUE;
86562}
86563static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut)
86564{
86565 int i;
86566 ma_uint32 riceParamMask;
86567 ma_int32* pDecodedSamples = pSamplesOut;
86568 ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
86569 ma_uint32 zeroCountParts0 = 0;
86570 ma_uint32 zeroCountParts1 = 0;
86571 ma_uint32 zeroCountParts2 = 0;
86572 ma_uint32 zeroCountParts3 = 0;
86573 ma_uint32 riceParamParts0 = 0;
86574 ma_uint32 riceParamParts1 = 0;
86575 ma_uint32 riceParamParts2 = 0;
86576 ma_uint32 riceParamParts3 = 0;
86577 __m128i coefficients128_0;
86578 __m128i coefficients128_4;
86579 __m128i coefficients128_8;
86580 __m128i samples128_0;
86581 __m128i samples128_4;
86582 __m128i samples128_8;
86583 __m128i prediction128;
86584 __m128i riceParamMask128;
86585 const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
86586 MA_DR_FLAC_ASSERT(order <= 12);
86587 riceParamMask = (ma_uint32)~((~0UL) << riceParam);
86588 riceParamMask128 = _mm_set1_epi32(riceParamMask);
86589 prediction128 = _mm_setzero_si128();
86590 coefficients128_0 = _mm_setzero_si128();
86591 coefficients128_4 = _mm_setzero_si128();
86592 coefficients128_8 = _mm_setzero_si128();
86593 samples128_0 = _mm_setzero_si128();
86594 samples128_4 = _mm_setzero_si128();
86595 samples128_8 = _mm_setzero_si128();
86596#if 1
86597 {
86598 int runningOrder = order;
86599 if (runningOrder >= 4) {
86600 coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));
86601 samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4));
86602 runningOrder -= 4;
86603 } else {
86604 switch (runningOrder) {
86605 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;
86606 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;
86607 case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break;
86608 }
86609 runningOrder = 0;
86610 }
86611 if (runningOrder >= 4) {
86612 coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));
86613 samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8));
86614 runningOrder -= 4;
86615 } else {
86616 switch (runningOrder) {
86617 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;
86618 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;
86619 case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break;
86620 }
86621 runningOrder = 0;
86622 }
86623 if (runningOrder == 4) {
86624 coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));
86625 samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12));
86626 runningOrder -= 4;
86627 } else {
86628 switch (runningOrder) {
86629 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;
86630 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;
86631 case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break;
86632 }
86633 runningOrder = 0;
86634 }
86635 coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));
86636 coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));
86637 coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));
86638 }
86639#else
86640 switch (order)
86641 {
86642 case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12];
86643 case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11];
86644 case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10];
86645 case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9];
86646 case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8];
86647 case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7];
86648 case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6];
86649 case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5];
86650 case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4];
86651 case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3];
86652 case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2];
86653 case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1];
86654 }
86655#endif
86656 while (pDecodedSamples < pDecodedSamplesEnd) {
86657 __m128i zeroCountPart128;
86658 __m128i riceParamPart128;
86659 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
86660 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
86661 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
86662 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
86663 return MA_FALSE;
86664 }
86665 zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
86666 riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
86667 riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
86668 riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
86669 riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1)));
86670 for (i = 0; i < 4; i += 1) {
86671 prediction128 = _mm_xor_si128(prediction128, prediction128);
86672 switch (order)
86673 {
86674 case 12:
86675 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))));
86676 case 10:
86677 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))));
86678 case 8:
86679 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))));
86680 case 6:
86681 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))));
86682 case 4:
86683 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))));
86684 case 2:
86685 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))));
86686 }
86687 prediction128 = ma_dr_flac__mm_hadd_epi64(prediction128);
86688 prediction128 = ma_dr_flac__mm_srai_epi64(prediction128, shift);
86689 prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
86690 samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4);
86691 samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
86692 samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
86693 riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
86694 }
86695 _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);
86696 pDecodedSamples += 4;
86697 }
86698 i = (count & ~3);
86699 while (i < (int)count) {
86700 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
86701 return MA_FALSE;
86702 }
86703 riceParamParts0 &= riceParamMask;
86704 riceParamParts0 |= (zeroCountParts0 << riceParam);
86705 riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
86706 pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);
86707 i += 1;
86708 pDecodedSamples += 1;
86709 }
86710 return MA_TRUE;
86711}
86712static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)
86713{
86714 MA_DR_FLAC_ASSERT(bs != NULL);
86715 MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
86716 if (lpcOrder > 0 && lpcOrder <= 12) {
86717 if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
86718 return ma_dr_flac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
86719 } else {
86720 return ma_dr_flac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
86721 }
86722 } else {
86723 return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
86724 }
86725}
86726#endif
86727#if defined(MA_DR_FLAC_SUPPORT_NEON)
86728static MA_INLINE void ma_dr_flac__vst2q_s32(ma_int32* p, int32x4x2_t x)
86729{
86730 vst1q_s32(p+0, x.val[0]);
86731 vst1q_s32(p+4, x.val[1]);
86732}
86733static MA_INLINE void ma_dr_flac__vst2q_u32(ma_uint32* p, uint32x4x2_t x)
86734{
86735 vst1q_u32(p+0, x.val[0]);
86736 vst1q_u32(p+4, x.val[1]);
86737}
86738static MA_INLINE void ma_dr_flac__vst2q_f32(float* p, float32x4x2_t x)
86739{
86740 vst1q_f32(p+0, x.val[0]);
86741 vst1q_f32(p+4, x.val[1]);
86742}
86743static MA_INLINE void ma_dr_flac__vst2q_s16(ma_int16* p, int16x4x2_t x)
86744{
86745 vst1q_s16(p, vcombine_s16(x.val[0], x.val[1]));
86746}
86747static MA_INLINE void ma_dr_flac__vst2q_u16(ma_uint16* p, uint16x4x2_t x)
86748{
86749 vst1q_u16(p, vcombine_u16(x.val[0], x.val[1]));
86750}
86751static MA_INLINE int32x4_t ma_dr_flac__vdupq_n_s32x4(ma_int32 x3, ma_int32 x2, ma_int32 x1, ma_int32 x0)
86752{
86753 ma_int32 x[4];
86754 x[3] = x3;
86755 x[2] = x2;
86756 x[1] = x1;
86757 x[0] = x0;
86758 return vld1q_s32(x);
86759}
86760static MA_INLINE int32x4_t ma_dr_flac__valignrq_s32_1(int32x4_t a, int32x4_t b)
86761{
86762 return vextq_s32(b, a, 1);
86763}
86764static MA_INLINE uint32x4_t ma_dr_flac__valignrq_u32_1(uint32x4_t a, uint32x4_t b)
86765{
86766 return vextq_u32(b, a, 1);
86767}
86768static MA_INLINE int32x2_t ma_dr_flac__vhaddq_s32(int32x4_t x)
86769{
86770 int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x));
86771 return vpadd_s32(r, r);
86772}
86773static MA_INLINE int64x1_t ma_dr_flac__vhaddq_s64(int64x2_t x)
86774{
86775 return vadd_s64(vget_high_s64(x), vget_low_s64(x));
86776}
86777static MA_INLINE int32x4_t ma_dr_flac__vrevq_s32(int32x4_t x)
86778{
86779 return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x)));
86780}
86781static MA_INLINE int32x4_t ma_dr_flac__vnotq_s32(int32x4_t x)
86782{
86783 return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF));
86784}
86785static MA_INLINE uint32x4_t ma_dr_flac__vnotq_u32(uint32x4_t x)
86786{
86787 return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF));
86788}
86789static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut)
86790{
86791 int i;
86792 ma_uint32 riceParamMask;
86793 ma_int32* pDecodedSamples = pSamplesOut;
86794 ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
86795 ma_uint32 zeroCountParts[4];
86796 ma_uint32 riceParamParts[4];
86797 int32x4_t coefficients128_0;
86798 int32x4_t coefficients128_4;
86799 int32x4_t coefficients128_8;
86800 int32x4_t samples128_0;
86801 int32x4_t samples128_4;
86802 int32x4_t samples128_8;
86803 uint32x4_t riceParamMask128;
86804 int32x4_t riceParam128;
86805 int32x2_t shift64;
86806 uint32x4_t one128;
86807 const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
86808 riceParamMask = (ma_uint32)~((~0UL) << riceParam);
86809 riceParamMask128 = vdupq_n_u32(riceParamMask);
86810 riceParam128 = vdupq_n_s32(riceParam);
86811 shift64 = vdup_n_s32(-shift);
86812 one128 = vdupq_n_u32(1);
86813 {
86814 int runningOrder = order;
86815 ma_int32 tempC[4] = {0, 0, 0, 0};
86816 ma_int32 tempS[4] = {0, 0, 0, 0};
86817 if (runningOrder >= 4) {
86818 coefficients128_0 = vld1q_s32(coefficients + 0);
86819 samples128_0 = vld1q_s32(pSamplesOut - 4);
86820 runningOrder -= 4;
86821 } else {
86822 switch (runningOrder) {
86823 case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3];
86824 case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2];
86825 case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1];
86826 }
86827 coefficients128_0 = vld1q_s32(tempC);
86828 samples128_0 = vld1q_s32(tempS);
86829 runningOrder = 0;
86830 }
86831 if (runningOrder >= 4) {
86832 coefficients128_4 = vld1q_s32(coefficients + 4);
86833 samples128_4 = vld1q_s32(pSamplesOut - 8);
86834 runningOrder -= 4;
86835 } else {
86836 switch (runningOrder) {
86837 case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7];
86838 case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6];
86839 case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5];
86840 }
86841 coefficients128_4 = vld1q_s32(tempC);
86842 samples128_4 = vld1q_s32(tempS);
86843 runningOrder = 0;
86844 }
86845 if (runningOrder == 4) {
86846 coefficients128_8 = vld1q_s32(coefficients + 8);
86847 samples128_8 = vld1q_s32(pSamplesOut - 12);
86848 runningOrder -= 4;
86849 } else {
86850 switch (runningOrder) {
86851 case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11];
86852 case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10];
86853 case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9];
86854 }
86855 coefficients128_8 = vld1q_s32(tempC);
86856 samples128_8 = vld1q_s32(tempS);
86857 runningOrder = 0;
86858 }
86859 coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0);
86860 coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4);
86861 coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8);
86862 }
86863 while (pDecodedSamples < pDecodedSamplesEnd) {
86864 int32x4_t prediction128;
86865 int32x2_t prediction64;
86866 uint32x4_t zeroCountPart128;
86867 uint32x4_t riceParamPart128;
86868 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||
86869 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||
86870 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||
86871 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {
86872 return MA_FALSE;
86873 }
86874 zeroCountPart128 = vld1q_u32(zeroCountParts);
86875 riceParamPart128 = vld1q_u32(riceParamParts);
86876 riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);
86877 riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));
86878 riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));
86879 if (order <= 4) {
86880 for (i = 0; i < 4; i += 1) {
86881 prediction128 = vmulq_s32(coefficients128_0, samples128_0);
86882 prediction64 = ma_dr_flac__vhaddq_s32(prediction128);
86883 prediction64 = vshl_s32(prediction64, shift64);
86884 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
86885 samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
86886 riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
86887 }
86888 } else if (order <= 8) {
86889 for (i = 0; i < 4; i += 1) {
86890 prediction128 = vmulq_s32(coefficients128_4, samples128_4);
86891 prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);
86892 prediction64 = ma_dr_flac__vhaddq_s32(prediction128);
86893 prediction64 = vshl_s32(prediction64, shift64);
86894 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
86895 samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4);
86896 samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
86897 riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
86898 }
86899 } else {
86900 for (i = 0; i < 4; i += 1) {
86901 prediction128 = vmulq_s32(coefficients128_8, samples128_8);
86902 prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4);
86903 prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);
86904 prediction64 = ma_dr_flac__vhaddq_s32(prediction128);
86905 prediction64 = vshl_s32(prediction64, shift64);
86906 prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
86907 samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8);
86908 samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4);
86909 samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
86910 riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
86911 }
86912 }
86913 vst1q_s32(pDecodedSamples, samples128_0);
86914 pDecodedSamples += 4;
86915 }
86916 i = (count & ~3);
86917 while (i < (int)count) {
86918 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {
86919 return MA_FALSE;
86920 }
86921 riceParamParts[0] &= riceParamMask;
86922 riceParamParts[0] |= (zeroCountParts[0] << riceParam);
86923 riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];
86924 pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);
86925 i += 1;
86926 pDecodedSamples += 1;
86927 }
86928 return MA_TRUE;
86929}
86930static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut)
86931{
86932 int i;
86933 ma_uint32 riceParamMask;
86934 ma_int32* pDecodedSamples = pSamplesOut;
86935 ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
86936 ma_uint32 zeroCountParts[4];
86937 ma_uint32 riceParamParts[4];
86938 int32x4_t coefficients128_0;
86939 int32x4_t coefficients128_4;
86940 int32x4_t coefficients128_8;
86941 int32x4_t samples128_0;
86942 int32x4_t samples128_4;
86943 int32x4_t samples128_8;
86944 uint32x4_t riceParamMask128;
86945 int32x4_t riceParam128;
86946 int64x1_t shift64;
86947 uint32x4_t one128;
86948 int64x2_t prediction128 = { 0 };
86949 uint32x4_t zeroCountPart128;
86950 uint32x4_t riceParamPart128;
86951 const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
86952 riceParamMask = (ma_uint32)~((~0UL) << riceParam);
86953 riceParamMask128 = vdupq_n_u32(riceParamMask);
86954 riceParam128 = vdupq_n_s32(riceParam);
86955 shift64 = vdup_n_s64(-shift);
86956 one128 = vdupq_n_u32(1);
86957 {
86958 int runningOrder = order;
86959 ma_int32 tempC[4] = {0, 0, 0, 0};
86960 ma_int32 tempS[4] = {0, 0, 0, 0};
86961 if (runningOrder >= 4) {
86962 coefficients128_0 = vld1q_s32(coefficients + 0);
86963 samples128_0 = vld1q_s32(pSamplesOut - 4);
86964 runningOrder -= 4;
86965 } else {
86966 switch (runningOrder) {
86967 case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3];
86968 case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2];
86969 case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1];
86970 }
86971 coefficients128_0 = vld1q_s32(tempC);
86972 samples128_0 = vld1q_s32(tempS);
86973 runningOrder = 0;
86974 }
86975 if (runningOrder >= 4) {
86976 coefficients128_4 = vld1q_s32(coefficients + 4);
86977 samples128_4 = vld1q_s32(pSamplesOut - 8);
86978 runningOrder -= 4;
86979 } else {
86980 switch (runningOrder) {
86981 case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7];
86982 case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6];
86983 case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5];
86984 }
86985 coefficients128_4 = vld1q_s32(tempC);
86986 samples128_4 = vld1q_s32(tempS);
86987 runningOrder = 0;
86988 }
86989 if (runningOrder == 4) {
86990 coefficients128_8 = vld1q_s32(coefficients + 8);
86991 samples128_8 = vld1q_s32(pSamplesOut - 12);
86992 runningOrder -= 4;
86993 } else {
86994 switch (runningOrder) {
86995 case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11];
86996 case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10];
86997 case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9];
86998 }
86999 coefficients128_8 = vld1q_s32(tempC);
87000 samples128_8 = vld1q_s32(tempS);
87001 runningOrder = 0;
87002 }
87003 coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0);
87004 coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4);
87005 coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8);
87006 }
87007 while (pDecodedSamples < pDecodedSamplesEnd) {
87008 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||
87009 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||
87010 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||
87011 !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {
87012 return MA_FALSE;
87013 }
87014 zeroCountPart128 = vld1q_u32(zeroCountParts);
87015 riceParamPart128 = vld1q_u32(riceParamParts);
87016 riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);
87017 riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));
87018 riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));
87019 for (i = 0; i < 4; i += 1) {
87020 int64x1_t prediction64;
87021 prediction128 = veorq_s64(prediction128, prediction128);
87022 switch (order)
87023 {
87024 case 12:
87025 case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8)));
87026 case 10:
87027 case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8)));
87028 case 8:
87029 case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4)));
87030 case 6:
87031 case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4)));
87032 case 4:
87033 case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0)));
87034 case 2:
87035 case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0)));
87036 }
87037 prediction64 = ma_dr_flac__vhaddq_s64(prediction128);
87038 prediction64 = vshl_s64(prediction64, shift64);
87039 prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0)));
87040 samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8);
87041 samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4);
87042 samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0);
87043 riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
87044 }
87045 vst1q_s32(pDecodedSamples, samples128_0);
87046 pDecodedSamples += 4;
87047 }
87048 i = (count & ~3);
87049 while (i < (int)count) {
87050 if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {
87051 return MA_FALSE;
87052 }
87053 riceParamParts[0] &= riceParamMask;
87054 riceParamParts[0] |= (zeroCountParts[0] << riceParam);
87055 riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];
87056 pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);
87057 i += 1;
87058 pDecodedSamples += 1;
87059 }
87060 return MA_TRUE;
87061}
87062static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)
87063{
87064 MA_DR_FLAC_ASSERT(bs != NULL);
87065 MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
87066 if (lpcOrder > 0 && lpcOrder <= 12) {
87067 if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
87068 return ma_dr_flac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
87069 } else {
87070 return ma_dr_flac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
87071 }
87072 } else {
87073 return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
87074 }
87075}
87076#endif
87077static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)
87078{
87079#if defined(MA_DR_FLAC_SUPPORT_SSE41)
87080 if (ma_dr_flac__gIsSSE41Supported) {
87081 return ma_dr_flac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
87082 } else
87083#elif defined(MA_DR_FLAC_SUPPORT_NEON)
87084 if (ma_dr_flac__gIsNEONSupported) {
87085 return ma_dr_flac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
87086 } else
87087#endif
87088 {
87089 #if 0
87090 return ma_dr_flac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
87091 #else
87092 return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
87093 #endif
87094 }
87095}
87096static ma_bool32 ma_dr_flac__read_and_seek_residual__rice(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam)
87097{
87098 ma_uint32 i;
87099 MA_DR_FLAC_ASSERT(bs != NULL);
87100 for (i = 0; i < count; ++i) {
87101 if (!ma_dr_flac__seek_rice_parts(bs, riceParam)) {
87102 return MA_FALSE;
87103 }
87104 }
87105 return MA_TRUE;
87106}
87107#if defined(__clang__)
87108__attribute__((no_sanitize("signed-integer-overflow")))
87109#endif
87110static ma_bool32 ma_dr_flac__decode_samples_with_residual__unencoded(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 unencodedBitsPerSample, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)
87111{
87112 ma_uint32 i;
87113 MA_DR_FLAC_ASSERT(bs != NULL);
87114 MA_DR_FLAC_ASSERT(unencodedBitsPerSample <= 31);
87115 MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
87116 for (i = 0; i < count; ++i) {
87117 if (unencodedBitsPerSample > 0) {
87118 if (!ma_dr_flac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) {
87119 return MA_FALSE;
87120 }
87121 } else {
87122 pSamplesOut[i] = 0;
87123 }
87124 if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
87125 pSamplesOut[i] += ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
87126 } else {
87127 pSamplesOut[i] += ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
87128 }
87129 }
87130 return MA_TRUE;
87131}
87132static ma_bool32 ma_dr_flac__decode_samples_with_residual(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 blockSize, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pDecodedSamples)
87133{
87134 ma_uint8 residualMethod;
87135 ma_uint8 partitionOrder;
87136 ma_uint32 samplesInPartition;
87137 ma_uint32 partitionsRemaining;
87138 MA_DR_FLAC_ASSERT(bs != NULL);
87139 MA_DR_FLAC_ASSERT(blockSize != 0);
87140 MA_DR_FLAC_ASSERT(pDecodedSamples != NULL);
87141 if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) {
87142 return MA_FALSE;
87143 }
87144 if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
87145 return MA_FALSE;
87146 }
87147 pDecodedSamples += lpcOrder;
87148 if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) {
87149 return MA_FALSE;
87150 }
87151 if (partitionOrder > 8) {
87152 return MA_FALSE;
87153 }
87154 if ((blockSize / (1 << partitionOrder)) < lpcOrder) {
87155 return MA_FALSE;
87156 }
87157 samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder;
87158 partitionsRemaining = (1 << partitionOrder);
87159 for (;;) {
87160 ma_uint8 riceParam = 0;
87161 if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
87162 if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) {
87163 return MA_FALSE;
87164 }
87165 if (riceParam == 15) {
87166 riceParam = 0xFF;
87167 }
87168 } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
87169 if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) {
87170 return MA_FALSE;
87171 }
87172 if (riceParam == 31) {
87173 riceParam = 0xFF;
87174 }
87175 }
87176 if (riceParam != 0xFF) {
87177 if (!ma_dr_flac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
87178 return MA_FALSE;
87179 }
87180 } else {
87181 ma_uint8 unencodedBitsPerSample = 0;
87182 if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
87183 return MA_FALSE;
87184 }
87185 if (!ma_dr_flac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
87186 return MA_FALSE;
87187 }
87188 }
87189 pDecodedSamples += samplesInPartition;
87190 if (partitionsRemaining == 1) {
87191 break;
87192 }
87193 partitionsRemaining -= 1;
87194 if (partitionOrder != 0) {
87195 samplesInPartition = blockSize / (1 << partitionOrder);
87196 }
87197 }
87198 return MA_TRUE;
87199}
87200static ma_bool32 ma_dr_flac__read_and_seek_residual(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 order)
87201{
87202 ma_uint8 residualMethod;
87203 ma_uint8 partitionOrder;
87204 ma_uint32 samplesInPartition;
87205 ma_uint32 partitionsRemaining;
87206 MA_DR_FLAC_ASSERT(bs != NULL);
87207 MA_DR_FLAC_ASSERT(blockSize != 0);
87208 if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) {
87209 return MA_FALSE;
87210 }
87211 if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
87212 return MA_FALSE;
87213 }
87214 if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) {
87215 return MA_FALSE;
87216 }
87217 if (partitionOrder > 8) {
87218 return MA_FALSE;
87219 }
87220 if ((blockSize / (1 << partitionOrder)) <= order) {
87221 return MA_FALSE;
87222 }
87223 samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
87224 partitionsRemaining = (1 << partitionOrder);
87225 for (;;)
87226 {
87227 ma_uint8 riceParam = 0;
87228 if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
87229 if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) {
87230 return MA_FALSE;
87231 }
87232 if (riceParam == 15) {
87233 riceParam = 0xFF;
87234 }
87235 } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
87236 if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) {
87237 return MA_FALSE;
87238 }
87239 if (riceParam == 31) {
87240 riceParam = 0xFF;
87241 }
87242 }
87243 if (riceParam != 0xFF) {
87244 if (!ma_dr_flac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) {
87245 return MA_FALSE;
87246 }
87247 } else {
87248 ma_uint8 unencodedBitsPerSample = 0;
87249 if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
87250 return MA_FALSE;
87251 }
87252 if (!ma_dr_flac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) {
87253 return MA_FALSE;
87254 }
87255 }
87256 if (partitionsRemaining == 1) {
87257 break;
87258 }
87259 partitionsRemaining -= 1;
87260 samplesInPartition = blockSize / (1 << partitionOrder);
87261 }
87262 return MA_TRUE;
87263}
87264static ma_bool32 ma_dr_flac__decode_samples__constant(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples)
87265{
87266 ma_uint32 i;
87267 ma_int32 sample;
87268 if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) {
87269 return MA_FALSE;
87270 }
87271 for (i = 0; i < blockSize; ++i) {
87272 pDecodedSamples[i] = sample;
87273 }
87274 return MA_TRUE;
87275}
87276static ma_bool32 ma_dr_flac__decode_samples__verbatim(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples)
87277{
87278 ma_uint32 i;
87279 for (i = 0; i < blockSize; ++i) {
87280 ma_int32 sample;
87281 if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) {
87282 return MA_FALSE;
87283 }
87284 pDecodedSamples[i] = sample;
87285 }
87286 return MA_TRUE;
87287}
87288static ma_bool32 ma_dr_flac__decode_samples__fixed(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples)
87289{
87290 ma_uint32 i;
87291 static ma_int32 lpcCoefficientsTable[5][4] = {
87292 {0, 0, 0, 0},
87293 {1, 0, 0, 0},
87294 {2, -1, 0, 0},
87295 {3, -3, 1, 0},
87296 {4, -6, 4, -1}
87297 };
87298 for (i = 0; i < lpcOrder; ++i) {
87299 ma_int32 sample;
87300 if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) {
87301 return MA_FALSE;
87302 }
87303 pDecodedSamples[i] = sample;
87304 }
87305 if (!ma_dr_flac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) {
87306 return MA_FALSE;
87307 }
87308 return MA_TRUE;
87309}
87310static ma_bool32 ma_dr_flac__decode_samples__lpc(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 bitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples)
87311{
87312 ma_uint8 i;
87313 ma_uint8 lpcPrecision;
87314 ma_int8 lpcShift;
87315 ma_int32 coefficients[32];
87316 for (i = 0; i < lpcOrder; ++i) {
87317 ma_int32 sample;
87318 if (!ma_dr_flac__read_int32(bs, bitsPerSample, &sample)) {
87319 return MA_FALSE;
87320 }
87321 pDecodedSamples[i] = sample;
87322 }
87323 if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) {
87324 return MA_FALSE;
87325 }
87326 if (lpcPrecision == 15) {
87327 return MA_FALSE;
87328 }
87329 lpcPrecision += 1;
87330 if (!ma_dr_flac__read_int8(bs, 5, &lpcShift)) {
87331 return MA_FALSE;
87332 }
87333 if (lpcShift < 0) {
87334 return MA_FALSE;
87335 }
87336 MA_DR_FLAC_ZERO_MEMORY(coefficients, sizeof(coefficients));
87337 for (i = 0; i < lpcOrder; ++i) {
87338 if (!ma_dr_flac__read_int32(bs, lpcPrecision, coefficients + i)) {
87339 return MA_FALSE;
87340 }
87341 }
87342 if (!ma_dr_flac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
87343 return MA_FALSE;
87344 }
87345 return MA_TRUE;
87346}
87347static ma_bool32 ma_dr_flac__read_next_flac_frame_header(ma_dr_flac_bs* bs, ma_uint8 streaminfoBitsPerSample, ma_dr_flac_frame_header* header)
87348{
87349 const ma_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000};
87350 const ma_uint8 bitsPerSampleTable[8] = {0, 8, 12, (ma_uint8)-1, 16, 20, 24, (ma_uint8)-1};
87351 MA_DR_FLAC_ASSERT(bs != NULL);
87352 MA_DR_FLAC_ASSERT(header != NULL);
87353 for (;;) {
87354 ma_uint8 crc8 = 0xCE;
87355 ma_uint8 reserved = 0;
87356 ma_uint8 blockingStrategy = 0;
87357 ma_uint8 blockSize = 0;
87358 ma_uint8 sampleRate = 0;
87359 ma_uint8 channelAssignment = 0;
87360 ma_uint8 bitsPerSample = 0;
87361 ma_bool32 isVariableBlockSize;
87362 if (!ma_dr_flac__find_and_seek_to_next_sync_code(bs)) {
87363 return MA_FALSE;
87364 }
87365 if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) {
87366 return MA_FALSE;
87367 }
87368 if (reserved == 1) {
87369 continue;
87370 }
87371 crc8 = ma_dr_flac_crc8(crc8, reserved, 1);
87372 if (!ma_dr_flac__read_uint8(bs, 1, &blockingStrategy)) {
87373 return MA_FALSE;
87374 }
87375 crc8 = ma_dr_flac_crc8(crc8, blockingStrategy, 1);
87376 if (!ma_dr_flac__read_uint8(bs, 4, &blockSize)) {
87377 return MA_FALSE;
87378 }
87379 if (blockSize == 0) {
87380 continue;
87381 }
87382 crc8 = ma_dr_flac_crc8(crc8, blockSize, 4);
87383 if (!ma_dr_flac__read_uint8(bs, 4, &sampleRate)) {
87384 return MA_FALSE;
87385 }
87386 crc8 = ma_dr_flac_crc8(crc8, sampleRate, 4);
87387 if (!ma_dr_flac__read_uint8(bs, 4, &channelAssignment)) {
87388 return MA_FALSE;
87389 }
87390 if (channelAssignment > 10) {
87391 continue;
87392 }
87393 crc8 = ma_dr_flac_crc8(crc8, channelAssignment, 4);
87394 if (!ma_dr_flac__read_uint8(bs, 3, &bitsPerSample)) {
87395 return MA_FALSE;
87396 }
87397 if (bitsPerSample == 3 || bitsPerSample == 7) {
87398 continue;
87399 }
87400 crc8 = ma_dr_flac_crc8(crc8, bitsPerSample, 3);
87401 if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) {
87402 return MA_FALSE;
87403 }
87404 if (reserved == 1) {
87405 continue;
87406 }
87407 crc8 = ma_dr_flac_crc8(crc8, reserved, 1);
87408 isVariableBlockSize = blockingStrategy == 1;
87409 if (isVariableBlockSize) {
87410 ma_uint64 pcmFrameNumber;
87411 ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8);
87412 if (result != MA_SUCCESS) {
87413 if (result == MA_AT_END) {
87414 return MA_FALSE;
87415 } else {
87416 continue;
87417 }
87418 }
87419 header->flacFrameNumber = 0;
87420 header->pcmFrameNumber = pcmFrameNumber;
87421 } else {
87422 ma_uint64 flacFrameNumber = 0;
87423 ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8);
87424 if (result != MA_SUCCESS) {
87425 if (result == MA_AT_END) {
87426 return MA_FALSE;
87427 } else {
87428 continue;
87429 }
87430 }
87431 header->flacFrameNumber = (ma_uint32)flacFrameNumber;
87432 header->pcmFrameNumber = 0;
87433 }
87434 MA_DR_FLAC_ASSERT(blockSize > 0);
87435 if (blockSize == 1) {
87436 header->blockSizeInPCMFrames = 192;
87437 } else if (blockSize <= 5) {
87438 MA_DR_FLAC_ASSERT(blockSize >= 2);
87439 header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2));
87440 } else if (blockSize == 6) {
87441 if (!ma_dr_flac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) {
87442 return MA_FALSE;
87443 }
87444 crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 8);
87445 header->blockSizeInPCMFrames += 1;
87446 } else if (blockSize == 7) {
87447 if (!ma_dr_flac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) {
87448 return MA_FALSE;
87449 }
87450 crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 16);
87451 if (header->blockSizeInPCMFrames == 0xFFFF) {
87452 return MA_FALSE;
87453 }
87454 header->blockSizeInPCMFrames += 1;
87455 } else {
87456 MA_DR_FLAC_ASSERT(blockSize >= 8);
87457 header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8));
87458 }
87459 if (sampleRate <= 11) {
87460 header->sampleRate = sampleRateTable[sampleRate];
87461 } else if (sampleRate == 12) {
87462 if (!ma_dr_flac__read_uint32(bs, 8, &header->sampleRate)) {
87463 return MA_FALSE;
87464 }
87465 crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 8);
87466 header->sampleRate *= 1000;
87467 } else if (sampleRate == 13) {
87468 if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) {
87469 return MA_FALSE;
87470 }
87471 crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16);
87472 } else if (sampleRate == 14) {
87473 if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) {
87474 return MA_FALSE;
87475 }
87476 crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16);
87477 header->sampleRate *= 10;
87478 } else {
87479 continue;
87480 }
87481 header->channelAssignment = channelAssignment;
87482 header->bitsPerSample = bitsPerSampleTable[bitsPerSample];
87483 if (header->bitsPerSample == 0) {
87484 header->bitsPerSample = streaminfoBitsPerSample;
87485 }
87486 if (header->bitsPerSample != streaminfoBitsPerSample) {
87487 return MA_FALSE;
87488 }
87489 if (!ma_dr_flac__read_uint8(bs, 8, &header->crc8)) {
87490 return MA_FALSE;
87491 }
87492#ifndef MA_DR_FLAC_NO_CRC
87493 if (header->crc8 != crc8) {
87494 continue;
87495 }
87496#endif
87497 return MA_TRUE;
87498 }
87499}
87500static ma_bool32 ma_dr_flac__read_subframe_header(ma_dr_flac_bs* bs, ma_dr_flac_subframe* pSubframe)
87501{
87502 ma_uint8 header;
87503 int type;
87504 if (!ma_dr_flac__read_uint8(bs, 8, &header)) {
87505 return MA_FALSE;
87506 }
87507 if ((header & 0x80) != 0) {
87508 return MA_FALSE;
87509 }
87510 pSubframe->lpcOrder = 0;
87511 type = (header & 0x7E) >> 1;
87512 if (type == 0) {
87513 pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_CONSTANT;
87514 } else if (type == 1) {
87515 pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_VERBATIM;
87516 } else {
87517 if ((type & 0x20) != 0) {
87518 pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_LPC;
87519 pSubframe->lpcOrder = (ma_uint8)(type & 0x1F) + 1;
87520 } else if ((type & 0x08) != 0) {
87521 pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_FIXED;
87522 pSubframe->lpcOrder = (ma_uint8)(type & 0x07);
87523 if (pSubframe->lpcOrder > 4) {
87524 pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED;
87525 pSubframe->lpcOrder = 0;
87526 }
87527 } else {
87528 pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED;
87529 }
87530 }
87531 if (pSubframe->subframeType == MA_DR_FLAC_SUBFRAME_RESERVED) {
87532 return MA_FALSE;
87533 }
87534 pSubframe->wastedBitsPerSample = 0;
87535 if ((header & 0x01) == 1) {
87536 unsigned int wastedBitsPerSample;
87537 if (!ma_dr_flac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) {
87538 return MA_FALSE;
87539 }
87540 pSubframe->wastedBitsPerSample = (ma_uint8)wastedBitsPerSample + 1;
87541 }
87542 return MA_TRUE;
87543}
87544static ma_bool32 ma_dr_flac__decode_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex, ma_int32* pDecodedSamplesOut)
87545{
87546 ma_dr_flac_subframe* pSubframe;
87547 ma_uint32 subframeBitsPerSample;
87548 MA_DR_FLAC_ASSERT(bs != NULL);
87549 MA_DR_FLAC_ASSERT(frame != NULL);
87550 pSubframe = frame->subframes + subframeIndex;
87551 if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) {
87552 return MA_FALSE;
87553 }
87554 subframeBitsPerSample = frame->header.bitsPerSample;
87555 if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
87556 subframeBitsPerSample += 1;
87557 } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
87558 subframeBitsPerSample += 1;
87559 }
87560 if (subframeBitsPerSample > 32) {
87561 return MA_FALSE;
87562 }
87563 if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
87564 return MA_FALSE;
87565 }
87566 subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
87567 pSubframe->pSamplesS32 = pDecodedSamplesOut;
87568 if (frame->header.blockSizeInPCMFrames < pSubframe->lpcOrder) {
87569 return MA_FALSE;
87570 }
87571 switch (pSubframe->subframeType)
87572 {
87573 case MA_DR_FLAC_SUBFRAME_CONSTANT:
87574 {
87575 ma_dr_flac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
87576 } break;
87577 case MA_DR_FLAC_SUBFRAME_VERBATIM:
87578 {
87579 ma_dr_flac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
87580 } break;
87581 case MA_DR_FLAC_SUBFRAME_FIXED:
87582 {
87583 ma_dr_flac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
87584 } break;
87585 case MA_DR_FLAC_SUBFRAME_LPC:
87586 {
87587 ma_dr_flac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
87588 } break;
87589 default: return MA_FALSE;
87590 }
87591 return MA_TRUE;
87592}
87593static ma_bool32 ma_dr_flac__seek_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex)
87594{
87595 ma_dr_flac_subframe* pSubframe;
87596 ma_uint32 subframeBitsPerSample;
87597 MA_DR_FLAC_ASSERT(bs != NULL);
87598 MA_DR_FLAC_ASSERT(frame != NULL);
87599 pSubframe = frame->subframes + subframeIndex;
87600 if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) {
87601 return MA_FALSE;
87602 }
87603 subframeBitsPerSample = frame->header.bitsPerSample;
87604 if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
87605 subframeBitsPerSample += 1;
87606 } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
87607 subframeBitsPerSample += 1;
87608 }
87609 if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
87610 return MA_FALSE;
87611 }
87612 subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
87613 pSubframe->pSamplesS32 = NULL;
87614 switch (pSubframe->subframeType)
87615 {
87616 case MA_DR_FLAC_SUBFRAME_CONSTANT:
87617 {
87618 if (!ma_dr_flac__seek_bits(bs, subframeBitsPerSample)) {
87619 return MA_FALSE;
87620 }
87621 } break;
87622 case MA_DR_FLAC_SUBFRAME_VERBATIM:
87623 {
87624 unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample;
87625 if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) {
87626 return MA_FALSE;
87627 }
87628 } break;
87629 case MA_DR_FLAC_SUBFRAME_FIXED:
87630 {
87631 unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;
87632 if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) {
87633 return MA_FALSE;
87634 }
87635 if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {
87636 return MA_FALSE;
87637 }
87638 } break;
87639 case MA_DR_FLAC_SUBFRAME_LPC:
87640 {
87641 ma_uint8 lpcPrecision;
87642 unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;
87643 if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) {
87644 return MA_FALSE;
87645 }
87646 if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) {
87647 return MA_FALSE;
87648 }
87649 if (lpcPrecision == 15) {
87650 return MA_FALSE;
87651 }
87652 lpcPrecision += 1;
87653 bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5;
87654 if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) {
87655 return MA_FALSE;
87656 }
87657 if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {
87658 return MA_FALSE;
87659 }
87660 } break;
87661 default: return MA_FALSE;
87662 }
87663 return MA_TRUE;
87664}
87665static MA_INLINE ma_uint8 ma_dr_flac__get_channel_count_from_channel_assignment(ma_int8 channelAssignment)
87666{
87667 ma_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2};
87668 MA_DR_FLAC_ASSERT(channelAssignment <= 10);
87669 return lookup[channelAssignment];
87670}
87671static ma_result ma_dr_flac__decode_flac_frame(ma_dr_flac* pFlac)
87672{
87673 int channelCount;
87674 int i;
87675 ma_uint8 paddingSizeInBits;
87676 ma_uint16 desiredCRC16;
87677#ifndef MA_DR_FLAC_NO_CRC
87678 ma_uint16 actualCRC16;
87679#endif
87680 MA_DR_FLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes));
87681 if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) {
87682 return MA_ERROR;
87683 }
87684 channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
87685 if (channelCount != (int)pFlac->channels) {
87686 return MA_ERROR;
87687 }
87688 for (i = 0; i < channelCount; ++i) {
87689 if (!ma_dr_flac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) {
87690 return MA_ERROR;
87691 }
87692 }
87693 paddingSizeInBits = (ma_uint8)(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7);
87694 if (paddingSizeInBits > 0) {
87695 ma_uint8 padding = 0;
87696 if (!ma_dr_flac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) {
87697 return MA_AT_END;
87698 }
87699 }
87700#ifndef MA_DR_FLAC_NO_CRC
87701 actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs);
87702#endif
87703 if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
87704 return MA_AT_END;
87705 }
87706#ifndef MA_DR_FLAC_NO_CRC
87707 if (actualCRC16 != desiredCRC16) {
87708 return MA_CRC_MISMATCH;
87709 }
87710#endif
87711 pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
87712 return MA_SUCCESS;
87713}
87714static ma_result ma_dr_flac__seek_flac_frame(ma_dr_flac* pFlac)
87715{
87716 int channelCount;
87717 int i;
87718 ma_uint16 desiredCRC16;
87719#ifndef MA_DR_FLAC_NO_CRC
87720 ma_uint16 actualCRC16;
87721#endif
87722 channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
87723 for (i = 0; i < channelCount; ++i) {
87724 if (!ma_dr_flac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) {
87725 return MA_ERROR;
87726 }
87727 }
87728 if (!ma_dr_flac__seek_bits(&pFlac->bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) {
87729 return MA_ERROR;
87730 }
87731#ifndef MA_DR_FLAC_NO_CRC
87732 actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs);
87733#endif
87734 if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
87735 return MA_AT_END;
87736 }
87737#ifndef MA_DR_FLAC_NO_CRC
87738 if (actualCRC16 != desiredCRC16) {
87739 return MA_CRC_MISMATCH;
87740 }
87741#endif
87742 return MA_SUCCESS;
87743}
87744static ma_bool32 ma_dr_flac__read_and_decode_next_flac_frame(ma_dr_flac* pFlac)
87745{
87746 MA_DR_FLAC_ASSERT(pFlac != NULL);
87747 for (;;) {
87748 ma_result result;
87749 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
87750 return MA_FALSE;
87751 }
87752 result = ma_dr_flac__decode_flac_frame(pFlac);
87753 if (result != MA_SUCCESS) {
87754 if (result == MA_CRC_MISMATCH) {
87755 continue;
87756 } else {
87757 return MA_FALSE;
87758 }
87759 }
87760 return MA_TRUE;
87761 }
87762}
87763static void ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(ma_dr_flac* pFlac, ma_uint64* pFirstPCMFrame, ma_uint64* pLastPCMFrame)
87764{
87765 ma_uint64 firstPCMFrame;
87766 ma_uint64 lastPCMFrame;
87767 MA_DR_FLAC_ASSERT(pFlac != NULL);
87768 firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber;
87769 if (firstPCMFrame == 0) {
87770 firstPCMFrame = ((ma_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames;
87771 }
87772 lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
87773 if (lastPCMFrame > 0) {
87774 lastPCMFrame -= 1;
87775 }
87776 if (pFirstPCMFrame) {
87777 *pFirstPCMFrame = firstPCMFrame;
87778 }
87779 if (pLastPCMFrame) {
87780 *pLastPCMFrame = lastPCMFrame;
87781 }
87782}
87783static ma_bool32 ma_dr_flac__seek_to_first_frame(ma_dr_flac* pFlac)
87784{
87785 ma_bool32 result;
87786 MA_DR_FLAC_ASSERT(pFlac != NULL);
87787 result = ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes);
87788 MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));
87789 pFlac->currentPCMFrame = 0;
87790 return result;
87791}
87792static MA_INLINE ma_result ma_dr_flac__seek_to_next_flac_frame(ma_dr_flac* pFlac)
87793{
87794 MA_DR_FLAC_ASSERT(pFlac != NULL);
87795 return ma_dr_flac__seek_flac_frame(pFlac);
87796}
87797static ma_uint64 ma_dr_flac__seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 pcmFramesToSeek)
87798{
87799 ma_uint64 pcmFramesRead = 0;
87800 while (pcmFramesToSeek > 0) {
87801 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
87802 if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {
87803 break;
87804 }
87805 } else {
87806 if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) {
87807 pcmFramesRead += pcmFramesToSeek;
87808 pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)pcmFramesToSeek;
87809 pcmFramesToSeek = 0;
87810 } else {
87811 pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining;
87812 pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining;
87813 pFlac->currentFLACFrame.pcmFramesRemaining = 0;
87814 }
87815 }
87816 }
87817 pFlac->currentPCMFrame += pcmFramesRead;
87818 return pcmFramesRead;
87819}
87820static ma_bool32 ma_dr_flac__seek_to_pcm_frame__brute_force(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)
87821{
87822 ma_bool32 isMidFrame = MA_FALSE;
87823 ma_uint64 runningPCMFrameCount;
87824 MA_DR_FLAC_ASSERT(pFlac != NULL);
87825 if (pcmFrameIndex >= pFlac->currentPCMFrame) {
87826 runningPCMFrameCount = pFlac->currentPCMFrame;
87827 if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
87828 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
87829 return MA_FALSE;
87830 }
87831 } else {
87832 isMidFrame = MA_TRUE;
87833 }
87834 } else {
87835 runningPCMFrameCount = 0;
87836 if (!ma_dr_flac__seek_to_first_frame(pFlac)) {
87837 return MA_FALSE;
87838 }
87839 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
87840 return MA_FALSE;
87841 }
87842 }
87843 for (;;) {
87844 ma_uint64 pcmFrameCountInThisFLACFrame;
87845 ma_uint64 firstPCMFrameInFLACFrame = 0;
87846 ma_uint64 lastPCMFrameInFLACFrame = 0;
87847 ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
87848 pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
87849 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {
87850 ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;
87851 if (!isMidFrame) {
87852 ma_result result = ma_dr_flac__decode_flac_frame(pFlac);
87853 if (result == MA_SUCCESS) {
87854 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
87855 } else {
87856 if (result == MA_CRC_MISMATCH) {
87857 goto next_iteration;
87858 } else {
87859 return MA_FALSE;
87860 }
87861 }
87862 } else {
87863 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
87864 }
87865 } else {
87866 if (!isMidFrame) {
87867 ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac);
87868 if (result == MA_SUCCESS) {
87869 runningPCMFrameCount += pcmFrameCountInThisFLACFrame;
87870 } else {
87871 if (result == MA_CRC_MISMATCH) {
87872 goto next_iteration;
87873 } else {
87874 return MA_FALSE;
87875 }
87876 }
87877 } else {
87878 runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;
87879 pFlac->currentFLACFrame.pcmFramesRemaining = 0;
87880 isMidFrame = MA_FALSE;
87881 }
87882 if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {
87883 return MA_TRUE;
87884 }
87885 }
87886 next_iteration:
87887 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
87888 return MA_FALSE;
87889 }
87890 }
87891}
87892#if !defined(MA_DR_FLAC_NO_CRC)
87893#define MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f
87894static ma_bool32 ma_dr_flac__seek_to_approximate_flac_frame_to_byte(ma_dr_flac* pFlac, ma_uint64 targetByte, ma_uint64 rangeLo, ma_uint64 rangeHi, ma_uint64* pLastSuccessfulSeekOffset)
87895{
87896 MA_DR_FLAC_ASSERT(pFlac != NULL);
87897 MA_DR_FLAC_ASSERT(pLastSuccessfulSeekOffset != NULL);
87898 MA_DR_FLAC_ASSERT(targetByte >= rangeLo);
87899 MA_DR_FLAC_ASSERT(targetByte <= rangeHi);
87900 *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes;
87901 for (;;) {
87902 ma_uint64 lastTargetByte = targetByte;
87903 if (!ma_dr_flac__seek_to_byte(&pFlac->bs, targetByte)) {
87904 if (targetByte == 0) {
87905 ma_dr_flac__seek_to_first_frame(pFlac);
87906 return MA_FALSE;
87907 }
87908 targetByte = rangeLo + ((rangeHi - rangeLo)/2);
87909 rangeHi = targetByte;
87910 } else {
87911 MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));
87912#if 1
87913 if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {
87914 targetByte = rangeLo + ((rangeHi - rangeLo)/2);
87915 rangeHi = targetByte;
87916 } else {
87917 break;
87918 }
87919#else
87920 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
87921 targetByte = rangeLo + ((rangeHi - rangeLo)/2);
87922 rangeHi = targetByte;
87923 } else {
87924 break;
87925 }
87926#endif
87927 }
87928 if(targetByte == lastTargetByte) {
87929 return MA_FALSE;
87930 }
87931 }
87932 ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);
87933 MA_DR_FLAC_ASSERT(targetByte <= rangeHi);
87934 *pLastSuccessfulSeekOffset = targetByte;
87935 return MA_TRUE;
87936}
87937static ma_bool32 ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 offset)
87938{
87939#if 0
87940 if (ma_dr_flac__decode_flac_frame(pFlac) != MA_SUCCESS) {
87941 if (ma_dr_flac__read_and_decode_next_flac_frame(pFlac) == MA_FALSE) {
87942 return MA_FALSE;
87943 }
87944 }
87945#endif
87946 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, offset) == offset;
87947}
87948static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex, ma_uint64 byteRangeLo, ma_uint64 byteRangeHi)
87949{
87950 ma_uint64 targetByte;
87951 ma_uint64 pcmRangeLo = pFlac->totalPCMFrameCount;
87952 ma_uint64 pcmRangeHi = 0;
87953 ma_uint64 lastSuccessfulSeekOffset = (ma_uint64)-1;
87954 ma_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo;
87955 ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;
87956 targetByte = byteRangeLo + (ma_uint64)(((ma_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO);
87957 if (targetByte > byteRangeHi) {
87958 targetByte = byteRangeHi;
87959 }
87960 for (;;) {
87961 if (ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) {
87962 ma_uint64 newPCMRangeLo;
87963 ma_uint64 newPCMRangeHi;
87964 ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi);
87965 if (pcmRangeLo == newPCMRangeLo) {
87966 if (!ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) {
87967 break;
87968 }
87969 if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {
87970 return MA_TRUE;
87971 } else {
87972 break;
87973 }
87974 }
87975 pcmRangeLo = newPCMRangeLo;
87976 pcmRangeHi = newPCMRangeHi;
87977 if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) {
87978 if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) {
87979 return MA_TRUE;
87980 } else {
87981 break;
87982 }
87983 } else {
87984 const float approxCompressionRatio = (ma_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((ma_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f);
87985 if (pcmRangeLo > pcmFrameIndex) {
87986 byteRangeHi = lastSuccessfulSeekOffset;
87987 if (byteRangeLo > byteRangeHi) {
87988 byteRangeLo = byteRangeHi;
87989 }
87990 targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2);
87991 if (targetByte < byteRangeLo) {
87992 targetByte = byteRangeLo;
87993 }
87994 } else {
87995 if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) {
87996 if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {
87997 return MA_TRUE;
87998 } else {
87999 break;
88000 }
88001 } else {
88002 byteRangeLo = lastSuccessfulSeekOffset;
88003 if (byteRangeHi < byteRangeLo) {
88004 byteRangeHi = byteRangeLo;
88005 }
88006 targetByte = lastSuccessfulSeekOffset + (ma_uint64)(((ma_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio);
88007 if (targetByte > byteRangeHi) {
88008 targetByte = byteRangeHi;
88009 }
88010 if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) {
88011 closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset;
88012 }
88013 }
88014 }
88015 }
88016 } else {
88017 break;
88018 }
88019 }
88020 ma_dr_flac__seek_to_first_frame(pFlac);
88021 return MA_FALSE;
88022}
88023static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)
88024{
88025 ma_uint64 byteRangeLo;
88026 ma_uint64 byteRangeHi;
88027 ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;
88028 if (ma_dr_flac__seek_to_first_frame(pFlac) == MA_FALSE) {
88029 return MA_FALSE;
88030 }
88031 if (pcmFrameIndex < seekForwardThreshold) {
88032 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex;
88033 }
88034 byteRangeLo = pFlac->firstFLACFramePosInBytes;
88035 byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f);
88036 return ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi);
88037}
88038#endif
88039static ma_bool32 ma_dr_flac__seek_to_pcm_frame__seek_table(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)
88040{
88041 ma_uint32 iClosestSeekpoint = 0;
88042 ma_bool32 isMidFrame = MA_FALSE;
88043 ma_uint64 runningPCMFrameCount;
88044 ma_uint32 iSeekpoint;
88045 MA_DR_FLAC_ASSERT(pFlac != NULL);
88046 if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) {
88047 return MA_FALSE;
88048 }
88049 if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) {
88050 return MA_FALSE;
88051 }
88052 for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
88053 if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) {
88054 break;
88055 }
88056 iClosestSeekpoint = iSeekpoint;
88057 }
88058 if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) {
88059 return MA_FALSE;
88060 }
88061 if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) {
88062 return MA_FALSE;
88063 }
88064#if !defined(MA_DR_FLAC_NO_CRC)
88065 if (pFlac->totalPCMFrameCount > 0) {
88066 ma_uint64 byteRangeLo;
88067 ma_uint64 byteRangeHi;
88068 byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f);
88069 byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset;
88070 if (iClosestSeekpoint < pFlac->seekpointCount-1) {
88071 ma_uint32 iNextSeekpoint = iClosestSeekpoint + 1;
88072 if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) {
88073 return MA_FALSE;
88074 }
88075 if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) {
88076 byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1;
88077 }
88078 }
88079 if (ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {
88080 if (ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
88081 ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);
88082 if (ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) {
88083 return MA_TRUE;
88084 }
88085 }
88086 }
88087 }
88088#endif
88089 if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) {
88090 runningPCMFrameCount = pFlac->currentPCMFrame;
88091 if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
88092 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
88093 return MA_FALSE;
88094 }
88095 } else {
88096 isMidFrame = MA_TRUE;
88097 }
88098 } else {
88099 runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame;
88100 if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {
88101 return MA_FALSE;
88102 }
88103 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
88104 return MA_FALSE;
88105 }
88106 }
88107 for (;;) {
88108 ma_uint64 pcmFrameCountInThisFLACFrame;
88109 ma_uint64 firstPCMFrameInFLACFrame = 0;
88110 ma_uint64 lastPCMFrameInFLACFrame = 0;
88111 ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
88112 pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
88113 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {
88114 ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;
88115 if (!isMidFrame) {
88116 ma_result result = ma_dr_flac__decode_flac_frame(pFlac);
88117 if (result == MA_SUCCESS) {
88118 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
88119 } else {
88120 if (result == MA_CRC_MISMATCH) {
88121 goto next_iteration;
88122 } else {
88123 return MA_FALSE;
88124 }
88125 }
88126 } else {
88127 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
88128 }
88129 } else {
88130 if (!isMidFrame) {
88131 ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac);
88132 if (result == MA_SUCCESS) {
88133 runningPCMFrameCount += pcmFrameCountInThisFLACFrame;
88134 } else {
88135 if (result == MA_CRC_MISMATCH) {
88136 goto next_iteration;
88137 } else {
88138 return MA_FALSE;
88139 }
88140 }
88141 } else {
88142 runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;
88143 pFlac->currentFLACFrame.pcmFramesRemaining = 0;
88144 isMidFrame = MA_FALSE;
88145 }
88146 if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {
88147 return MA_TRUE;
88148 }
88149 }
88150 next_iteration:
88151 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
88152 return MA_FALSE;
88153 }
88154 }
88155}
88156#ifndef MA_DR_FLAC_NO_OGG
88157typedef struct
88158{
88159 ma_uint8 capturePattern[4];
88160 ma_uint8 structureVersion;
88161 ma_uint8 headerType;
88162 ma_uint64 granulePosition;
88163 ma_uint32 serialNumber;
88164 ma_uint32 sequenceNumber;
88165 ma_uint32 checksum;
88166 ma_uint8 segmentCount;
88167 ma_uint8 segmentTable[255];
88168} ma_dr_flac_ogg_page_header;
88169#endif
88170typedef struct
88171{
88172 ma_dr_flac_read_proc onRead;
88173 ma_dr_flac_seek_proc onSeek;
88174 ma_dr_flac_tell_proc onTell;
88175 ma_dr_flac_meta_proc onMeta;
88176 ma_dr_flac_container container;
88177 void* pUserData;
88178 void* pUserDataMD;
88179 ma_uint32 sampleRate;
88180 ma_uint8 channels;
88181 ma_uint8 bitsPerSample;
88182 ma_uint64 totalPCMFrameCount;
88183 ma_uint16 maxBlockSizeInPCMFrames;
88184 ma_uint64 runningFilePos;
88185 ma_bool32 hasStreamInfoBlock;
88186 ma_bool32 hasMetadataBlocks;
88187 ma_dr_flac_bs bs;
88188 ma_dr_flac_frame_header firstFrameHeader;
88189#ifndef MA_DR_FLAC_NO_OGG
88190 ma_uint32 oggSerial;
88191 ma_uint64 oggFirstBytePos;
88192 ma_dr_flac_ogg_page_header oggBosHeader;
88193#endif
88194} ma_dr_flac_init_info;
88195static MA_INLINE void ma_dr_flac__decode_block_header(ma_uint32 blockHeader, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize)
88196{
88197 blockHeader = ma_dr_flac__be2host_32(blockHeader);
88198 *isLastBlock = (ma_uint8)((blockHeader & 0x80000000UL) >> 31);
88199 *blockType = (ma_uint8)((blockHeader & 0x7F000000UL) >> 24);
88200 *blockSize = (blockHeader & 0x00FFFFFFUL);
88201}
88202static MA_INLINE ma_bool32 ma_dr_flac__read_and_decode_block_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize)
88203{
88204 ma_uint32 blockHeader;
88205 *blockSize = 0;
88206 if (onRead(pUserData, &blockHeader, 4) != 4) {
88207 return MA_FALSE;
88208 }
88209 ma_dr_flac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize);
88210 return MA_TRUE;
88211}
88212static ma_bool32 ma_dr_flac__read_streaminfo(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_streaminfo* pStreamInfo)
88213{
88214 ma_uint32 blockSizes;
88215 ma_uint64 frameSizes = 0;
88216 ma_uint64 importantProps;
88217 ma_uint8 md5[16];
88218 if (onRead(pUserData, &blockSizes, 4) != 4) {
88219 return MA_FALSE;
88220 }
88221 if (onRead(pUserData, &frameSizes, 6) != 6) {
88222 return MA_FALSE;
88223 }
88224 if (onRead(pUserData, &importantProps, 8) != 8) {
88225 return MA_FALSE;
88226 }
88227 if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) {
88228 return MA_FALSE;
88229 }
88230 blockSizes = ma_dr_flac__be2host_32(blockSizes);
88231 frameSizes = ma_dr_flac__be2host_64(frameSizes);
88232 importantProps = ma_dr_flac__be2host_64(importantProps);
88233 pStreamInfo->minBlockSizeInPCMFrames = (ma_uint16)((blockSizes & 0xFFFF0000) >> 16);
88234 pStreamInfo->maxBlockSizeInPCMFrames = (ma_uint16) (blockSizes & 0x0000FFFF);
88235 pStreamInfo->minFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 24)) >> 40);
88236 pStreamInfo->maxFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 0)) >> 16);
88237 pStreamInfo->sampleRate = (ma_uint32)((importantProps & (((ma_uint64)0x000FFFFF << 16) << 28)) >> 44);
88238 pStreamInfo->channels = (ma_uint8 )((importantProps & (((ma_uint64)0x0000000E << 16) << 24)) >> 41) + 1;
88239 pStreamInfo->bitsPerSample = (ma_uint8 )((importantProps & (((ma_uint64)0x0000001F << 16) << 20)) >> 36) + 1;
88240 pStreamInfo->totalPCMFrameCount = ((importantProps & ((((ma_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF)));
88241 MA_DR_FLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5));
88242 return MA_TRUE;
88243}
88244static void* ma_dr_flac__malloc_default(size_t sz, void* pUserData)
88245{
88246 (void)pUserData;
88247 return MA_DR_FLAC_MALLOC(sz);
88248}
88249static void* ma_dr_flac__realloc_default(void* p, size_t sz, void* pUserData)
88250{
88251 (void)pUserData;
88252 return MA_DR_FLAC_REALLOC(p, sz);
88253}
88254static void ma_dr_flac__free_default(void* p, void* pUserData)
88255{
88256 (void)pUserData;
88257 MA_DR_FLAC_FREE(p);
88258}
88259static void* ma_dr_flac__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
88260{
88261 if (pAllocationCallbacks == NULL) {
88262 return NULL;
88263 }
88264 if (pAllocationCallbacks->onMalloc != NULL) {
88265 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
88266 }
88267 if (pAllocationCallbacks->onRealloc != NULL) {
88268 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
88269 }
88270 return NULL;
88271}
88272static void* ma_dr_flac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks)
88273{
88274 if (pAllocationCallbacks == NULL) {
88275 return NULL;
88276 }
88277 if (pAllocationCallbacks->onRealloc != NULL) {
88278 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
88279 }
88280 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
88281 void* p2;
88282 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
88283 if (p2 == NULL) {
88284 return NULL;
88285 }
88286 if (p != NULL) {
88287 MA_DR_FLAC_COPY_MEMORY(p2, p, szOld);
88288 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
88289 }
88290 return p2;
88291 }
88292 return NULL;
88293}
88294static void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
88295{
88296 if (p == NULL || pAllocationCallbacks == NULL) {
88297 return;
88298 }
88299 if (pAllocationCallbacks->onFree != NULL) {
88300 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
88301 }
88302}
88303static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks)
88304{
88305 ma_uint64 runningFilePos = 42;
88306 ma_uint64 seektablePos = 0;
88307 ma_uint32 seektableSize = 0;
88308 (void)onTell;
88309 for (;;) {
88310 ma_dr_flac_metadata metadata;
88311 ma_uint8 isLastBlock = 0;
88312 ma_uint8 blockType = 0;
88313 ma_uint32 blockSize;
88314 if (ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == MA_FALSE) {
88315 return MA_FALSE;
88316 }
88317 runningFilePos += 4;
88318 metadata.type = blockType;
88319 metadata.pRawData = NULL;
88320 metadata.rawDataSize = 0;
88321 switch (blockType)
88322 {
88323 case MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION:
88324 {
88325 if (blockSize < 4) {
88326 return MA_FALSE;
88327 }
88328 if (onMeta) {
88329 void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
88330 if (pRawData == NULL) {
88331 return MA_FALSE;
88332 }
88333 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
88334 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88335 return MA_FALSE;
88336 }
88337 metadata.pRawData = pRawData;
88338 metadata.rawDataSize = blockSize;
88339 metadata.data.application.id = ma_dr_flac__be2host_32(*(ma_uint32*)pRawData);
88340 metadata.data.application.pData = (const void*)((ma_uint8*)pRawData + sizeof(ma_uint32));
88341 metadata.data.application.dataSize = blockSize - sizeof(ma_uint32);
88342 onMeta(pUserDataMD, &metadata);
88343 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88344 }
88345 } break;
88346 case MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE:
88347 {
88348 seektablePos = runningFilePos;
88349 seektableSize = blockSize;
88350 if (onMeta) {
88351 ma_uint32 seekpointCount;
88352 ma_uint32 iSeekpoint;
88353 void* pRawData;
88354 seekpointCount = blockSize/MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES;
88355 pRawData = ma_dr_flac__malloc_from_callbacks(seekpointCount * sizeof(ma_dr_flac_seekpoint), pAllocationCallbacks);
88356 if (pRawData == NULL) {
88357 return MA_FALSE;
88358 }
88359 for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) {
88360 ma_dr_flac_seekpoint* pSeekpoint = (ma_dr_flac_seekpoint*)pRawData + iSeekpoint;
88361 if (onRead(pUserData, pSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) != MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) {
88362 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88363 return MA_FALSE;
88364 }
88365 pSeekpoint->firstPCMFrame = ma_dr_flac__be2host_64(pSeekpoint->firstPCMFrame);
88366 pSeekpoint->flacFrameOffset = ma_dr_flac__be2host_64(pSeekpoint->flacFrameOffset);
88367 pSeekpoint->pcmFrameCount = ma_dr_flac__be2host_16(pSeekpoint->pcmFrameCount);
88368 }
88369 metadata.pRawData = pRawData;
88370 metadata.rawDataSize = blockSize;
88371 metadata.data.seektable.seekpointCount = seekpointCount;
88372 metadata.data.seektable.pSeekpoints = (const ma_dr_flac_seekpoint*)pRawData;
88373 onMeta(pUserDataMD, &metadata);
88374 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88375 }
88376 } break;
88377 case MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT:
88378 {
88379 if (blockSize < 8) {
88380 return MA_FALSE;
88381 }
88382 if (onMeta) {
88383 void* pRawData;
88384 const char* pRunningData;
88385 const char* pRunningDataEnd;
88386 ma_uint32 i;
88387 pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
88388 if (pRawData == NULL) {
88389 return MA_FALSE;
88390 }
88391 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
88392 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88393 return MA_FALSE;
88394 }
88395 metadata.pRawData = pRawData;
88396 metadata.rawDataSize = blockSize;
88397 pRunningData = (const char*)pRawData;
88398 pRunningDataEnd = (const char*)pRawData + blockSize;
88399 metadata.data.vorbis_comment.vendorLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
88400 if ((pRunningDataEnd - pRunningData) - 4 < (ma_int64)metadata.data.vorbis_comment.vendorLength) {
88401 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88402 return MA_FALSE;
88403 }
88404 metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength;
88405 metadata.data.vorbis_comment.commentCount = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
88406 if ((pRunningDataEnd - pRunningData) / sizeof(ma_uint32) < metadata.data.vorbis_comment.commentCount) {
88407 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88408 return MA_FALSE;
88409 }
88410 metadata.data.vorbis_comment.pComments = pRunningData;
88411 for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) {
88412 ma_uint32 commentLength;
88413 if (pRunningDataEnd - pRunningData < 4) {
88414 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88415 return MA_FALSE;
88416 }
88417 commentLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
88418 if (pRunningDataEnd - pRunningData < (ma_int64)commentLength) {
88419 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88420 return MA_FALSE;
88421 }
88422 pRunningData += commentLength;
88423 }
88424 onMeta(pUserDataMD, &metadata);
88425 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88426 }
88427 } break;
88428 case MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET:
88429 {
88430 if (blockSize < 396) {
88431 return MA_FALSE;
88432 }
88433 if (onMeta) {
88434 void* pRawData;
88435 const char* pRunningData;
88436 const char* pRunningDataEnd;
88437 size_t bufferSize;
88438 ma_uint8 iTrack;
88439 ma_uint8 iIndex;
88440 void* pTrackData;
88441 pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
88442 if (pRawData == NULL) {
88443 return MA_FALSE;
88444 }
88445 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
88446 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88447 return MA_FALSE;
88448 }
88449 metadata.pRawData = pRawData;
88450 metadata.rawDataSize = blockSize;
88451 pRunningData = (const char*)pRawData;
88452 pRunningDataEnd = (const char*)pRawData + blockSize;
88453 MA_DR_FLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128;
88454 metadata.data.cuesheet.leadInSampleCount = ma_dr_flac__be2host_64(*(const ma_uint64*)pRunningData); pRunningData += 8;
88455 metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259;
88456 metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1;
88457 metadata.data.cuesheet.pTrackData = NULL;
88458 {
88459 const char* pRunningDataSaved = pRunningData;
88460 bufferSize = metadata.data.cuesheet.trackCount * MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES;
88461 for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) {
88462 ma_uint8 indexCount;
88463 ma_uint32 indexPointSize;
88464 if (pRunningDataEnd - pRunningData < MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES) {
88465 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88466 return MA_FALSE;
88467 }
88468 pRunningData += 35;
88469 indexCount = pRunningData[0];
88470 pRunningData += 1;
88471 bufferSize += indexCount * sizeof(ma_dr_flac_cuesheet_track_index);
88472 indexPointSize = indexCount * MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES;
88473 if (pRunningDataEnd - pRunningData < (ma_int64)indexPointSize) {
88474 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88475 return MA_FALSE;
88476 }
88477 pRunningData += indexPointSize;
88478 }
88479 pRunningData = pRunningDataSaved;
88480 }
88481 {
88482 char* pRunningTrackData;
88483 pTrackData = ma_dr_flac__malloc_from_callbacks(bufferSize, pAllocationCallbacks);
88484 if (pTrackData == NULL) {
88485 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88486 return MA_FALSE;
88487 }
88488 pRunningTrackData = (char*)pTrackData;
88489 for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) {
88490 ma_uint8 indexCount;
88491 MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES);
88492 pRunningData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1;
88493 pRunningTrackData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1;
88494 indexCount = pRunningData[0];
88495 pRunningData += 1;
88496 pRunningTrackData += 1;
88497 for (iIndex = 0; iIndex < indexCount; ++iIndex) {
88498 ma_dr_flac_cuesheet_track_index* pTrackIndex = (ma_dr_flac_cuesheet_track_index*)pRunningTrackData;
88499 MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES);
88500 pRunningData += MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES;
88501 pRunningTrackData += sizeof(ma_dr_flac_cuesheet_track_index);
88502 pTrackIndex->offset = ma_dr_flac__be2host_64(pTrackIndex->offset);
88503 }
88504 }
88505 metadata.data.cuesheet.pTrackData = pTrackData;
88506 }
88507 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88508 pRawData = NULL;
88509 onMeta(pUserDataMD, &metadata);
88510 ma_dr_flac__free_from_callbacks(pTrackData, pAllocationCallbacks);
88511 pTrackData = NULL;
88512 }
88513 } break;
88514 case MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE:
88515 {
88516 if (blockSize < 32) {
88517 return MA_FALSE;
88518 }
88519 if (onMeta) {
88520 void* pRawData;
88521 const char* pRunningData;
88522 const char* pRunningDataEnd;
88523 pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
88524 if (pRawData == NULL) {
88525 return MA_FALSE;
88526 }
88527 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
88528 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88529 return MA_FALSE;
88530 }
88531 metadata.pRawData = pRawData;
88532 metadata.rawDataSize = blockSize;
88533 pRunningData = (const char*)pRawData;
88534 pRunningDataEnd = (const char*)pRawData + blockSize;
88535 metadata.data.picture.type = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
88536 metadata.data.picture.mimeLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
88537 if ((pRunningDataEnd - pRunningData) - 24 < (ma_int64)metadata.data.picture.mimeLength) {
88538 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88539 return MA_FALSE;
88540 }
88541 metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength;
88542 metadata.data.picture.descriptionLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
88543 if ((pRunningDataEnd - pRunningData) - 20 < (ma_int64)metadata.data.picture.descriptionLength) {
88544 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88545 return MA_FALSE;
88546 }
88547 metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength;
88548 metadata.data.picture.width = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
88549 metadata.data.picture.height = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
88550 metadata.data.picture.colorDepth = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
88551 metadata.data.picture.indexColorCount = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
88552 metadata.data.picture.pictureDataSize = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
88553 metadata.data.picture.pPictureData = (const ma_uint8*)pRunningData;
88554 if (pRunningDataEnd - pRunningData < (ma_int64)metadata.data.picture.pictureDataSize) {
88555 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88556 return MA_FALSE;
88557 }
88558 onMeta(pUserDataMD, &metadata);
88559 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88560 }
88561 } break;
88562 case MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING:
88563 {
88564 if (onMeta) {
88565 metadata.data.padding.unused = 0;
88566 if (!onSeek(pUserData, blockSize, MA_DR_FLAC_SEEK_CUR)) {
88567 isLastBlock = MA_TRUE;
88568 } else {
88569 onMeta(pUserDataMD, &metadata);
88570 }
88571 }
88572 } break;
88573 case MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID:
88574 {
88575 if (onMeta) {
88576 if (!onSeek(pUserData, blockSize, MA_DR_FLAC_SEEK_CUR)) {
88577 isLastBlock = MA_TRUE;
88578 }
88579 }
88580 } break;
88581 default:
88582 {
88583 if (onMeta) {
88584 void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
88585 if (pRawData == NULL) {
88586 return MA_FALSE;
88587 }
88588 if (onRead(pUserData, pRawData, blockSize) != blockSize) {
88589 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88590 return MA_FALSE;
88591 }
88592 metadata.pRawData = pRawData;
88593 metadata.rawDataSize = blockSize;
88594 onMeta(pUserDataMD, &metadata);
88595 ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
88596 }
88597 } break;
88598 }
88599 if (onMeta == NULL && blockSize > 0) {
88600 if (!onSeek(pUserData, blockSize, MA_DR_FLAC_SEEK_CUR)) {
88601 isLastBlock = MA_TRUE;
88602 }
88603 }
88604 runningFilePos += blockSize;
88605 if (isLastBlock) {
88606 break;
88607 }
88608 }
88609 *pSeektablePos = seektablePos;
88610 *pSeekpointCount = seektableSize / MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES;
88611 *pFirstFramePos = runningFilePos;
88612 return MA_TRUE;
88613}
88614static ma_bool32 ma_dr_flac__init_private__native(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed)
88615{
88616 ma_uint8 isLastBlock;
88617 ma_uint8 blockType;
88618 ma_uint32 blockSize;
88619 (void)onSeek;
88620 pInit->container = ma_dr_flac_container_native;
88621 if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
88622 return MA_FALSE;
88623 }
88624 if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
88625 if (!relaxed) {
88626 return MA_FALSE;
88627 } else {
88628 pInit->hasStreamInfoBlock = MA_FALSE;
88629 pInit->hasMetadataBlocks = MA_FALSE;
88630 if (!ma_dr_flac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) {
88631 return MA_FALSE;
88632 }
88633 if (pInit->firstFrameHeader.bitsPerSample == 0) {
88634 return MA_FALSE;
88635 }
88636 pInit->sampleRate = pInit->firstFrameHeader.sampleRate;
88637 pInit->channels = ma_dr_flac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment);
88638 pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample;
88639 pInit->maxBlockSizeInPCMFrames = 65535;
88640 return MA_TRUE;
88641 }
88642 } else {
88643 ma_dr_flac_streaminfo streaminfo;
88644 if (!ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) {
88645 return MA_FALSE;
88646 }
88647 pInit->hasStreamInfoBlock = MA_TRUE;
88648 pInit->sampleRate = streaminfo.sampleRate;
88649 pInit->channels = streaminfo.channels;
88650 pInit->bitsPerSample = streaminfo.bitsPerSample;
88651 pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount;
88652 pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;
88653 pInit->hasMetadataBlocks = !isLastBlock;
88654 if (onMeta) {
88655 ma_dr_flac_metadata metadata;
88656 metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO;
88657 metadata.pRawData = NULL;
88658 metadata.rawDataSize = 0;
88659 metadata.data.streaminfo = streaminfo;
88660 onMeta(pUserDataMD, &metadata);
88661 }
88662 return MA_TRUE;
88663 }
88664}
88665#ifndef MA_DR_FLAC_NO_OGG
88666#define MA_DR_FLAC_OGG_MAX_PAGE_SIZE 65307
88667#define MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199
88668typedef enum
88669{
88670 ma_dr_flac_ogg_recover_on_crc_mismatch,
88671 ma_dr_flac_ogg_fail_on_crc_mismatch
88672} ma_dr_flac_ogg_crc_mismatch_recovery;
88673#ifndef MA_DR_FLAC_NO_CRC
88674static ma_uint32 ma_dr_flac__crc32_table[] = {
88675 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L,
88676 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L,
88677 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L,
88678 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL,
88679 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L,
88680 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L,
88681 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L,
88682 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL,
88683 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L,
88684 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L,
88685 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L,
88686 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL,
88687 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L,
88688 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L,
88689 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L,
88690 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL,
88691 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL,
88692 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L,
88693 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L,
88694 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL,
88695 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL,
88696 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L,
88697 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L,
88698 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL,
88699 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL,
88700 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L,
88701 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L,
88702 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL,
88703 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL,
88704 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L,
88705 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L,
88706 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL,
88707 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L,
88708 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL,
88709 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL,
88710 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L,
88711 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L,
88712 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL,
88713 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL,
88714 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L,
88715 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L,
88716 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL,
88717 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL,
88718 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L,
88719 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L,
88720 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL,
88721 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL,
88722 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L,
88723 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L,
88724 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL,
88725 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L,
88726 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L,
88727 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L,
88728 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL,
88729 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L,
88730 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L,
88731 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L,
88732 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL,
88733 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L,
88734 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L,
88735 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L,
88736 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL,
88737 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L,
88738 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L
88739};
88740#endif
88741static MA_INLINE ma_uint32 ma_dr_flac_crc32_byte(ma_uint32 crc32, ma_uint8 data)
88742{
88743#ifndef MA_DR_FLAC_NO_CRC
88744 return (crc32 << 8) ^ ma_dr_flac__crc32_table[(ma_uint8)((crc32 >> 24) & 0xFF) ^ data];
88745#else
88746 (void)data;
88747 return crc32;
88748#endif
88749}
88750#if 0
88751static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint32(ma_uint32 crc32, ma_uint32 data)
88752{
88753 crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 24) & 0xFF));
88754 crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 16) & 0xFF));
88755 crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 8) & 0xFF));
88756 crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 0) & 0xFF));
88757 return crc32;
88758}
88759static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint64(ma_uint32 crc32, ma_uint64 data)
88760{
88761 crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 32) & 0xFFFFFFFF));
88762 crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 0) & 0xFFFFFFFF));
88763 return crc32;
88764}
88765#endif
88766static MA_INLINE ma_uint32 ma_dr_flac_crc32_buffer(ma_uint32 crc32, ma_uint8* pData, ma_uint32 dataSize)
88767{
88768 ma_uint32 i;
88769 for (i = 0; i < dataSize; ++i) {
88770 crc32 = ma_dr_flac_crc32_byte(crc32, pData[i]);
88771 }
88772 return crc32;
88773}
88774static MA_INLINE ma_bool32 ma_dr_flac_ogg__is_capture_pattern(ma_uint8 pattern[4])
88775{
88776 return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S';
88777}
88778static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_header_size(ma_dr_flac_ogg_page_header* pHeader)
88779{
88780 return 27 + pHeader->segmentCount;
88781}
88782static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_body_size(ma_dr_flac_ogg_page_header* pHeader)
88783{
88784 ma_uint32 pageBodySize = 0;
88785 int i;
88786 for (i = 0; i < pHeader->segmentCount; ++i) {
88787 pageBodySize += pHeader->segmentTable[i];
88788 }
88789 return pageBodySize;
88790}
88791static ma_result ma_dr_flac_ogg__read_page_header_after_capture_pattern(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32)
88792{
88793 ma_uint8 data[23];
88794 ma_uint32 i;
88795 MA_DR_FLAC_ASSERT(*pCRC32 == MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32);
88796 if (onRead(pUserData, data, 23) != 23) {
88797 return MA_AT_END;
88798 }
88799 *pBytesRead += 23;
88800 pHeader->capturePattern[0] = 'O';
88801 pHeader->capturePattern[1] = 'g';
88802 pHeader->capturePattern[2] = 'g';
88803 pHeader->capturePattern[3] = 'S';
88804 pHeader->structureVersion = data[0];
88805 pHeader->headerType = data[1];
88806 MA_DR_FLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8);
88807 MA_DR_FLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4);
88808 MA_DR_FLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4);
88809 MA_DR_FLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4);
88810 pHeader->segmentCount = data[22];
88811 data[18] = 0;
88812 data[19] = 0;
88813 data[20] = 0;
88814 data[21] = 0;
88815 for (i = 0; i < 23; ++i) {
88816 *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, data[i]);
88817 }
88818 if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) {
88819 return MA_AT_END;
88820 }
88821 *pBytesRead += pHeader->segmentCount;
88822 for (i = 0; i < pHeader->segmentCount; ++i) {
88823 *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, pHeader->segmentTable[i]);
88824 }
88825 return MA_SUCCESS;
88826}
88827static ma_result ma_dr_flac_ogg__read_page_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32)
88828{
88829 ma_uint8 id[4];
88830 *pBytesRead = 0;
88831 if (onRead(pUserData, id, 4) != 4) {
88832 return MA_AT_END;
88833 }
88834 *pBytesRead += 4;
88835 for (;;) {
88836 if (ma_dr_flac_ogg__is_capture_pattern(id)) {
88837 ma_result result;
88838 *pCRC32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32;
88839 result = ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32);
88840 if (result == MA_SUCCESS) {
88841 return MA_SUCCESS;
88842 } else {
88843 if (result == MA_CRC_MISMATCH) {
88844 continue;
88845 } else {
88846 return result;
88847 }
88848 }
88849 } else {
88850 id[0] = id[1];
88851 id[1] = id[2];
88852 id[2] = id[3];
88853 if (onRead(pUserData, &id[3], 1) != 1) {
88854 return MA_AT_END;
88855 }
88856 *pBytesRead += 1;
88857 }
88858 }
88859}
88860typedef struct
88861{
88862 ma_dr_flac_read_proc onRead;
88863 ma_dr_flac_seek_proc onSeek;
88864 ma_dr_flac_tell_proc onTell;
88865 void* pUserData;
88866 ma_uint64 currentBytePos;
88867 ma_uint64 firstBytePos;
88868 ma_uint32 serialNumber;
88869 ma_dr_flac_ogg_page_header bosPageHeader;
88870 ma_dr_flac_ogg_page_header currentPageHeader;
88871 ma_uint32 bytesRemainingInPage;
88872 ma_uint32 pageDataSize;
88873 ma_uint8 pageData[MA_DR_FLAC_OGG_MAX_PAGE_SIZE];
88874} ma_dr_flac_oggbs;
88875static size_t ma_dr_flac_oggbs__read_physical(ma_dr_flac_oggbs* oggbs, void* bufferOut, size_t bytesToRead)
88876{
88877 size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead);
88878 oggbs->currentBytePos += bytesActuallyRead;
88879 return bytesActuallyRead;
88880}
88881static ma_bool32 ma_dr_flac_oggbs__seek_physical(ma_dr_flac_oggbs* oggbs, ma_uint64 offset, ma_dr_flac_seek_origin origin)
88882{
88883 if (origin == MA_DR_FLAC_SEEK_SET) {
88884 if (offset <= 0x7FFFFFFF) {
88885 if (!oggbs->onSeek(oggbs->pUserData, (int)offset, MA_DR_FLAC_SEEK_SET)) {
88886 return MA_FALSE;
88887 }
88888 oggbs->currentBytePos = offset;
88889 return MA_TRUE;
88890 } else {
88891 if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, MA_DR_FLAC_SEEK_SET)) {
88892 return MA_FALSE;
88893 }
88894 oggbs->currentBytePos = offset;
88895 return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, MA_DR_FLAC_SEEK_CUR);
88896 }
88897 } else {
88898 while (offset > 0x7FFFFFFF) {
88899 if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, MA_DR_FLAC_SEEK_CUR)) {
88900 return MA_FALSE;
88901 }
88902 oggbs->currentBytePos += 0x7FFFFFFF;
88903 offset -= 0x7FFFFFFF;
88904 }
88905 if (!oggbs->onSeek(oggbs->pUserData, (int)offset, MA_DR_FLAC_SEEK_CUR)) {
88906 return MA_FALSE;
88907 }
88908 oggbs->currentBytePos += offset;
88909 return MA_TRUE;
88910 }
88911}
88912static ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr_flac_ogg_crc_mismatch_recovery recoveryMethod)
88913{
88914 ma_dr_flac_ogg_page_header header;
88915 for (;;) {
88916 ma_uint32 crc32 = 0;
88917 ma_uint32 bytesRead;
88918 ma_uint32 pageBodySize;
88919#ifndef MA_DR_FLAC_NO_CRC
88920 ma_uint32 actualCRC32;
88921#endif
88922 if (ma_dr_flac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) {
88923 return MA_FALSE;
88924 }
88925 oggbs->currentBytePos += bytesRead;
88926 pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header);
88927 if (pageBodySize > MA_DR_FLAC_OGG_MAX_PAGE_SIZE) {
88928 continue;
88929 }
88930 if (header.serialNumber != oggbs->serialNumber) {
88931 if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, MA_DR_FLAC_SEEK_CUR)) {
88932 return MA_FALSE;
88933 }
88934 continue;
88935 }
88936 if (ma_dr_flac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) {
88937 return MA_FALSE;
88938 }
88939 oggbs->pageDataSize = pageBodySize;
88940#ifndef MA_DR_FLAC_NO_CRC
88941 actualCRC32 = ma_dr_flac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize);
88942 if (actualCRC32 != header.checksum) {
88943 if (recoveryMethod == ma_dr_flac_ogg_recover_on_crc_mismatch) {
88944 continue;
88945 } else {
88946 ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch);
88947 return MA_FALSE;
88948 }
88949 }
88950#else
88951 (void)recoveryMethod;
88952#endif
88953 oggbs->currentPageHeader = header;
88954 oggbs->bytesRemainingInPage = pageBodySize;
88955 return MA_TRUE;
88956 }
88957}
88958#if 0
88959static ma_uint8 ma_dr_flac_oggbs__get_current_segment_index(ma_dr_flac_oggbs* oggbs, ma_uint8* pBytesRemainingInSeg)
88960{
88961 ma_uint32 bytesConsumedInPage = ma_dr_flac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage;
88962 ma_uint8 iSeg = 0;
88963 ma_uint32 iByte = 0;
88964 while (iByte < bytesConsumedInPage) {
88965 ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
88966 if (iByte + segmentSize > bytesConsumedInPage) {
88967 break;
88968 } else {
88969 iSeg += 1;
88970 iByte += segmentSize;
88971 }
88972 }
88973 *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (ma_uint8)(bytesConsumedInPage - iByte);
88974 return iSeg;
88975}
88976static ma_bool32 ma_dr_flac_oggbs__seek_to_next_packet(ma_dr_flac_oggbs* oggbs)
88977{
88978 for (;;) {
88979 ma_bool32 atEndOfPage = MA_FALSE;
88980 ma_uint8 bytesRemainingInSeg;
88981 ma_uint8 iFirstSeg = ma_dr_flac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg);
88982 ma_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg;
88983 for (ma_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) {
88984 ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
88985 if (segmentSize < 255) {
88986 if (iSeg == oggbs->currentPageHeader.segmentCount-1) {
88987 atEndOfPage = MA_TRUE;
88988 }
88989 break;
88990 }
88991 bytesToEndOfPacketOrPage += segmentSize;
88992 }
88993 ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, MA_DR_FLAC_SEEK_CUR);
88994 oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage;
88995 if (atEndOfPage) {
88996 if (!ma_dr_flac_oggbs__goto_next_page(oggbs)) {
88997 return MA_FALSE;
88998 }
88999 if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
89000 return MA_TRUE;
89001 }
89002 } else {
89003 return MA_TRUE;
89004 }
89005 }
89006}
89007static ma_bool32 ma_dr_flac_oggbs__seek_to_next_frame(ma_dr_flac_oggbs* oggbs)
89008{
89009 return ma_dr_flac_oggbs__seek_to_next_packet(oggbs);
89010}
89011#endif
89012static size_t ma_dr_flac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead)
89013{
89014 ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData;
89015 ma_uint8* pRunningBufferOut = (ma_uint8*)bufferOut;
89016 size_t bytesRead = 0;
89017 MA_DR_FLAC_ASSERT(oggbs != NULL);
89018 MA_DR_FLAC_ASSERT(pRunningBufferOut != NULL);
89019 while (bytesRead < bytesToRead) {
89020 size_t bytesRemainingToRead = bytesToRead - bytesRead;
89021 if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) {
89022 MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead);
89023 bytesRead += bytesRemainingToRead;
89024 oggbs->bytesRemainingInPage -= (ma_uint32)bytesRemainingToRead;
89025 break;
89026 }
89027 if (oggbs->bytesRemainingInPage > 0) {
89028 MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage);
89029 bytesRead += oggbs->bytesRemainingInPage;
89030 pRunningBufferOut += oggbs->bytesRemainingInPage;
89031 oggbs->bytesRemainingInPage = 0;
89032 }
89033 MA_DR_FLAC_ASSERT(bytesRemainingToRead > 0);
89034 if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) {
89035 break;
89036 }
89037 }
89038 return bytesRead;
89039}
89040static ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac_seek_origin origin)
89041{
89042 ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData;
89043 int bytesSeeked = 0;
89044 MA_DR_FLAC_ASSERT(oggbs != NULL);
89045 MA_DR_FLAC_ASSERT(offset >= 0);
89046 if (origin == MA_DR_FLAC_SEEK_SET) {
89047 if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, MA_DR_FLAC_SEEK_SET)) {
89048 return MA_FALSE;
89049 }
89050 if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) {
89051 return MA_FALSE;
89052 }
89053 return ma_dr_flac__on_seek_ogg(pUserData, offset, MA_DR_FLAC_SEEK_CUR);
89054 } else if (origin == MA_DR_FLAC_SEEK_CUR) {
89055 while (bytesSeeked < offset) {
89056 int bytesRemainingToSeek = offset - bytesSeeked;
89057 MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0);
89058 if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) {
89059 bytesSeeked += bytesRemainingToSeek;
89060 (void)bytesSeeked;
89061 oggbs->bytesRemainingInPage -= bytesRemainingToSeek;
89062 break;
89063 }
89064 if (oggbs->bytesRemainingInPage > 0) {
89065 bytesSeeked += (int)oggbs->bytesRemainingInPage;
89066 oggbs->bytesRemainingInPage = 0;
89067 }
89068 MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0);
89069 if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) {
89070 return MA_FALSE;
89071 }
89072 }
89073 } else if (origin == MA_DR_FLAC_SEEK_END) {
89074 return MA_FALSE;
89075 }
89076 return MA_TRUE;
89077}
89078static ma_bool32 ma_dr_flac__on_tell_ogg(void* pUserData, ma_int64* pCursor)
89079{
89080 (void)pUserData;
89081 (void)pCursor;
89082 return MA_FALSE;
89083}
89084static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)
89085{
89086 ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs;
89087 ma_uint64 originalBytePos;
89088 ma_uint64 runningGranulePosition;
89089 ma_uint64 runningFrameBytePos;
89090 ma_uint64 runningPCMFrameCount;
89091 MA_DR_FLAC_ASSERT(oggbs != NULL);
89092 originalBytePos = oggbs->currentBytePos;
89093 if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) {
89094 return MA_FALSE;
89095 }
89096 oggbs->bytesRemainingInPage = 0;
89097 runningGranulePosition = 0;
89098 for (;;) {
89099 if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) {
89100 ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, MA_DR_FLAC_SEEK_SET);
89101 return MA_FALSE;
89102 }
89103 runningFrameBytePos = oggbs->currentBytePos - ma_dr_flac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize;
89104 if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) {
89105 break;
89106 }
89107 if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
89108 if (oggbs->currentPageHeader.segmentTable[0] >= 2) {
89109 ma_uint8 firstBytesInPage[2];
89110 firstBytesInPage[0] = oggbs->pageData[0];
89111 firstBytesInPage[1] = oggbs->pageData[1];
89112 if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) {
89113 runningGranulePosition = oggbs->currentPageHeader.granulePosition;
89114 }
89115 continue;
89116 }
89117 }
89118 }
89119 if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, MA_DR_FLAC_SEEK_SET)) {
89120 return MA_FALSE;
89121 }
89122 if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) {
89123 return MA_FALSE;
89124 }
89125 runningPCMFrameCount = runningGranulePosition;
89126 for (;;) {
89127 ma_uint64 firstPCMFrameInFLACFrame = 0;
89128 ma_uint64 lastPCMFrameInFLACFrame = 0;
89129 ma_uint64 pcmFrameCountInThisFrame;
89130 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
89131 return MA_FALSE;
89132 }
89133 ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
89134 pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
89135 if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) {
89136 ma_result result = ma_dr_flac__decode_flac_frame(pFlac);
89137 if (result == MA_SUCCESS) {
89138 pFlac->currentPCMFrame = pcmFrameIndex;
89139 pFlac->currentFLACFrame.pcmFramesRemaining = 0;
89140 return MA_TRUE;
89141 } else {
89142 return MA_FALSE;
89143 }
89144 }
89145 if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) {
89146 ma_result result = ma_dr_flac__decode_flac_frame(pFlac);
89147 if (result == MA_SUCCESS) {
89148 ma_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount);
89149 if (pcmFramesToDecode == 0) {
89150 return MA_TRUE;
89151 }
89152 pFlac->currentPCMFrame = runningPCMFrameCount;
89153 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
89154 } else {
89155 if (result == MA_CRC_MISMATCH) {
89156 continue;
89157 } else {
89158 return MA_FALSE;
89159 }
89160 }
89161 } else {
89162 ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac);
89163 if (result == MA_SUCCESS) {
89164 runningPCMFrameCount += pcmFrameCountInThisFrame;
89165 } else {
89166 if (result == MA_CRC_MISMATCH) {
89167 continue;
89168 } else {
89169 return MA_FALSE;
89170 }
89171 }
89172 }
89173 }
89174}
89175static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed)
89176{
89177 ma_dr_flac_ogg_page_header header;
89178 ma_uint32 crc32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32;
89179 ma_uint32 bytesRead = 0;
89180 (void)relaxed;
89181 pInit->container = ma_dr_flac_container_ogg;
89182 pInit->oggFirstBytePos = 0;
89183 if (ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) {
89184 return MA_FALSE;
89185 }
89186 pInit->runningFilePos += bytesRead;
89187 for (;;) {
89188 int pageBodySize;
89189 if ((header.headerType & 0x02) == 0) {
89190 return MA_FALSE;
89191 }
89192 pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header);
89193 if (pageBodySize == 51) {
89194 ma_uint32 bytesRemainingInPage = pageBodySize;
89195 ma_uint8 packetType;
89196 if (onRead(pUserData, &packetType, 1) != 1) {
89197 return MA_FALSE;
89198 }
89199 bytesRemainingInPage -= 1;
89200 if (packetType == 0x7F) {
89201 ma_uint8 sig[4];
89202 if (onRead(pUserData, sig, 4) != 4) {
89203 return MA_FALSE;
89204 }
89205 bytesRemainingInPage -= 4;
89206 if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') {
89207 ma_uint8 mappingVersion[2];
89208 if (onRead(pUserData, mappingVersion, 2) != 2) {
89209 return MA_FALSE;
89210 }
89211 if (mappingVersion[0] != 1) {
89212 return MA_FALSE;
89213 }
89214 if (!onSeek(pUserData, 2, MA_DR_FLAC_SEEK_CUR)) {
89215 return MA_FALSE;
89216 }
89217 if (onRead(pUserData, sig, 4) != 4) {
89218 return MA_FALSE;
89219 }
89220 if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') {
89221 ma_dr_flac_streaminfo streaminfo;
89222 ma_uint8 isLastBlock;
89223 ma_uint8 blockType;
89224 ma_uint32 blockSize;
89225 if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
89226 return MA_FALSE;
89227 }
89228 if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
89229 return MA_FALSE;
89230 }
89231 if (ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) {
89232 pInit->hasStreamInfoBlock = MA_TRUE;
89233 pInit->sampleRate = streaminfo.sampleRate;
89234 pInit->channels = streaminfo.channels;
89235 pInit->bitsPerSample = streaminfo.bitsPerSample;
89236 pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount;
89237 pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;
89238 pInit->hasMetadataBlocks = !isLastBlock;
89239 if (onMeta) {
89240 ma_dr_flac_metadata metadata;
89241 metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO;
89242 metadata.pRawData = NULL;
89243 metadata.rawDataSize = 0;
89244 metadata.data.streaminfo = streaminfo;
89245 onMeta(pUserDataMD, &metadata);
89246 }
89247 pInit->runningFilePos += pageBodySize;
89248 pInit->oggFirstBytePos = pInit->runningFilePos - 79;
89249 pInit->oggSerial = header.serialNumber;
89250 pInit->oggBosHeader = header;
89251 break;
89252 } else {
89253 return MA_FALSE;
89254 }
89255 } else {
89256 return MA_FALSE;
89257 }
89258 } else {
89259 if (!onSeek(pUserData, bytesRemainingInPage, MA_DR_FLAC_SEEK_CUR)) {
89260 return MA_FALSE;
89261 }
89262 }
89263 } else {
89264 if (!onSeek(pUserData, bytesRemainingInPage, MA_DR_FLAC_SEEK_CUR)) {
89265 return MA_FALSE;
89266 }
89267 }
89268 } else {
89269 if (!onSeek(pUserData, pageBodySize, MA_DR_FLAC_SEEK_CUR)) {
89270 return MA_FALSE;
89271 }
89272 }
89273 pInit->runningFilePos += pageBodySize;
89274 if (ma_dr_flac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) {
89275 return MA_FALSE;
89276 }
89277 pInit->runningFilePos += bytesRead;
89278 }
89279 pInit->hasMetadataBlocks = MA_TRUE;
89280 return MA_TRUE;
89281}
89282#endif
89283static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD)
89284{
89285 ma_bool32 relaxed;
89286 ma_uint8 id[4];
89287 if (pInit == NULL || onRead == NULL || onSeek == NULL) {
89288 return MA_FALSE;
89289 }
89290 MA_DR_FLAC_ZERO_MEMORY(pInit, sizeof(*pInit));
89291 pInit->onRead = onRead;
89292 pInit->onSeek = onSeek;
89293 pInit->onTell = onTell;
89294 pInit->onMeta = onMeta;
89295 pInit->container = container;
89296 pInit->pUserData = pUserData;
89297 pInit->pUserDataMD = pUserDataMD;
89298 pInit->bs.onRead = onRead;
89299 pInit->bs.onSeek = onSeek;
89300 pInit->bs.onTell = onTell;
89301 pInit->bs.pUserData = pUserData;
89302 ma_dr_flac__reset_cache(&pInit->bs);
89303 relaxed = container != ma_dr_flac_container_unknown;
89304 for (;;) {
89305 if (onRead(pUserData, id, 4) != 4) {
89306 return MA_FALSE;
89307 }
89308 pInit->runningFilePos += 4;
89309 if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') {
89310 ma_uint8 header[6];
89311 ma_uint8 flags;
89312 ma_uint32 headerSize;
89313 if (onRead(pUserData, header, 6) != 6) {
89314 return MA_FALSE;
89315 }
89316 pInit->runningFilePos += 6;
89317 flags = header[1];
89318 MA_DR_FLAC_COPY_MEMORY(&headerSize, header+2, 4);
89319 headerSize = ma_dr_flac__unsynchsafe_32(ma_dr_flac__be2host_32(headerSize));
89320 if (flags & 0x10) {
89321 headerSize += 10;
89322 }
89323 if (!onSeek(pUserData, headerSize, MA_DR_FLAC_SEEK_CUR)) {
89324 return MA_FALSE;
89325 }
89326 pInit->runningFilePos += headerSize;
89327 } else {
89328 break;
89329 }
89330 }
89331 if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') {
89332 return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
89333 }
89334#ifndef MA_DR_FLAC_NO_OGG
89335 if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') {
89336 return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
89337 }
89338#endif
89339 if (relaxed) {
89340 if (container == ma_dr_flac_container_native) {
89341 return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
89342 }
89343#ifndef MA_DR_FLAC_NO_OGG
89344 if (container == ma_dr_flac_container_ogg) {
89345 return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
89346 }
89347#endif
89348 }
89349 return MA_FALSE;
89350}
89351static void ma_dr_flac__init_from_info(ma_dr_flac* pFlac, const ma_dr_flac_init_info* pInit)
89352{
89353 MA_DR_FLAC_ASSERT(pFlac != NULL);
89354 MA_DR_FLAC_ASSERT(pInit != NULL);
89355 MA_DR_FLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac));
89356 pFlac->bs = pInit->bs;
89357 pFlac->onMeta = pInit->onMeta;
89358 pFlac->pUserDataMD = pInit->pUserDataMD;
89359 pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames;
89360 pFlac->sampleRate = pInit->sampleRate;
89361 pFlac->channels = (ma_uint8)pInit->channels;
89362 pFlac->bitsPerSample = (ma_uint8)pInit->bitsPerSample;
89363 pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount;
89364 pFlac->container = pInit->container;
89365}
89366static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks)
89367{
89368 ma_dr_flac_init_info init;
89369 ma_uint32 allocationSize;
89370 ma_uint32 wholeSIMDVectorCountPerChannel;
89371 ma_uint32 decodedSamplesAllocationSize;
89372#ifndef MA_DR_FLAC_NO_OGG
89373 ma_dr_flac_oggbs* pOggbs = NULL;
89374#endif
89375 ma_uint64 firstFramePos;
89376 ma_uint64 seektablePos;
89377 ma_uint32 seekpointCount;
89378 ma_allocation_callbacks allocationCallbacks;
89379 ma_dr_flac* pFlac;
89380 ma_dr_flac__init_cpu_caps();
89381 if (!ma_dr_flac__init_private(&init, onRead, onSeek, onTell, onMeta, container, pUserData, pUserDataMD)) {
89382 return NULL;
89383 }
89384 if (pAllocationCallbacks != NULL) {
89385 allocationCallbacks = *pAllocationCallbacks;
89386 if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) {
89387 return NULL;
89388 }
89389 } else {
89390 allocationCallbacks.pUserData = NULL;
89391 allocationCallbacks.onMalloc = ma_dr_flac__malloc_default;
89392 allocationCallbacks.onRealloc = ma_dr_flac__realloc_default;
89393 allocationCallbacks.onFree = ma_dr_flac__free_default;
89394 }
89395 allocationSize = sizeof(ma_dr_flac);
89396 if ((init.maxBlockSizeInPCMFrames % (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) == 0) {
89397 wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32)));
89398 } else {
89399 wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) + 1;
89400 }
89401 decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE * init.channels;
89402 allocationSize += decodedSamplesAllocationSize;
89403 allocationSize += MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE;
89404#ifndef MA_DR_FLAC_NO_OGG
89405 if (init.container == ma_dr_flac_container_ogg) {
89406 allocationSize += sizeof(ma_dr_flac_oggbs);
89407 pOggbs = (ma_dr_flac_oggbs*)ma_dr_flac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks);
89408 if (pOggbs == NULL) {
89409 return NULL;
89410 }
89411 MA_DR_FLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs));
89412 pOggbs->onRead = onRead;
89413 pOggbs->onSeek = onSeek;
89414 pOggbs->onTell = onTell;
89415 pOggbs->pUserData = pUserData;
89416 pOggbs->currentBytePos = init.oggFirstBytePos;
89417 pOggbs->firstBytePos = init.oggFirstBytePos;
89418 pOggbs->serialNumber = init.oggSerial;
89419 pOggbs->bosPageHeader = init.oggBosHeader;
89420 pOggbs->bytesRemainingInPage = 0;
89421 }
89422#endif
89423 firstFramePos = 42;
89424 seektablePos = 0;
89425 seekpointCount = 0;
89426 if (init.hasMetadataBlocks) {
89427 ma_dr_flac_read_proc onReadOverride = onRead;
89428 ma_dr_flac_seek_proc onSeekOverride = onSeek;
89429 ma_dr_flac_tell_proc onTellOverride = onTell;
89430 void* pUserDataOverride = pUserData;
89431#ifndef MA_DR_FLAC_NO_OGG
89432 if (init.container == ma_dr_flac_container_ogg) {
89433 onReadOverride = ma_dr_flac__on_read_ogg;
89434 onSeekOverride = ma_dr_flac__on_seek_ogg;
89435 onTellOverride = ma_dr_flac__on_tell_ogg;
89436 pUserDataOverride = (void*)pOggbs;
89437 }
89438#endif
89439 if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onTellOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) {
89440 #ifndef MA_DR_FLAC_NO_OGG
89441 ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks);
89442 #endif
89443 return NULL;
89444 }
89445 allocationSize += seekpointCount * sizeof(ma_dr_flac_seekpoint);
89446 }
89447 pFlac = (ma_dr_flac*)ma_dr_flac__malloc_from_callbacks(allocationSize, &allocationCallbacks);
89448 if (pFlac == NULL) {
89449 #ifndef MA_DR_FLAC_NO_OGG
89450 ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks);
89451 #endif
89452 return NULL;
89453 }
89454 ma_dr_flac__init_from_info(pFlac, &init);
89455 pFlac->allocationCallbacks = allocationCallbacks;
89456 pFlac->pDecodedSamples = (ma_int32*)ma_dr_flac_align((size_t)pFlac->pExtraData, MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE);
89457#ifndef MA_DR_FLAC_NO_OGG
89458 if (init.container == ma_dr_flac_container_ogg) {
89459 ma_dr_flac_oggbs* pInternalOggbs = (ma_dr_flac_oggbs*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(ma_dr_flac_seekpoint)));
89460 MA_DR_FLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs));
89461 ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks);
89462 pOggbs = NULL;
89463 pFlac->bs.onRead = ma_dr_flac__on_read_ogg;
89464 pFlac->bs.onSeek = ma_dr_flac__on_seek_ogg;
89465 pFlac->bs.onTell = ma_dr_flac__on_tell_ogg;
89466 pFlac->bs.pUserData = (void*)pInternalOggbs;
89467 pFlac->_oggbs = (void*)pInternalOggbs;
89468 }
89469#endif
89470 pFlac->firstFLACFramePosInBytes = firstFramePos;
89471#ifndef MA_DR_FLAC_NO_OGG
89472 if (init.container == ma_dr_flac_container_ogg)
89473 {
89474 pFlac->pSeekpoints = NULL;
89475 pFlac->seekpointCount = 0;
89476 }
89477 else
89478#endif
89479 {
89480 if (seektablePos != 0) {
89481 pFlac->seekpointCount = seekpointCount;
89482 pFlac->pSeekpoints = (ma_dr_flac_seekpoint*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize);
89483 MA_DR_FLAC_ASSERT(pFlac->bs.onSeek != NULL);
89484 MA_DR_FLAC_ASSERT(pFlac->bs.onRead != NULL);
89485 if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, MA_DR_FLAC_SEEK_SET)) {
89486 ma_uint32 iSeekpoint;
89487 for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) {
89488 if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) == MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) {
89489 pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame);
89490 pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset);
89491 pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = ma_dr_flac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount);
89492 } else {
89493 pFlac->pSeekpoints = NULL;
89494 pFlac->seekpointCount = 0;
89495 break;
89496 }
89497 }
89498 if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, MA_DR_FLAC_SEEK_SET)) {
89499 ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks);
89500 return NULL;
89501 }
89502 } else {
89503 pFlac->pSeekpoints = NULL;
89504 pFlac->seekpointCount = 0;
89505 }
89506 }
89507 }
89508 if (!init.hasStreamInfoBlock) {
89509 pFlac->currentFLACFrame.header = init.firstFrameHeader;
89510 for (;;) {
89511 ma_result result = ma_dr_flac__decode_flac_frame(pFlac);
89512 if (result == MA_SUCCESS) {
89513 break;
89514 } else {
89515 if (result == MA_CRC_MISMATCH) {
89516 if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
89517 ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks);
89518 return NULL;
89519 }
89520 continue;
89521 } else {
89522 ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks);
89523 return NULL;
89524 }
89525 }
89526 }
89527 }
89528 return pFlac;
89529}
89530#ifndef MA_DR_FLAC_NO_STDIO
89531#include <stdio.h>
89532#ifndef MA_DR_FLAC_NO_WCHAR
89533#include <wchar.h>
89534#endif
89535static size_t ma_dr_flac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
89536{
89537 return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData);
89538}
89539static ma_bool32 ma_dr_flac__on_seek_stdio(void* pUserData, int offset, ma_dr_flac_seek_origin origin)
89540{
89541 int whence = SEEK_SET;
89542 if (origin == MA_DR_FLAC_SEEK_CUR) {
89543 whence = SEEK_CUR;
89544 } else if (origin == MA_DR_FLAC_SEEK_END) {
89545 whence = SEEK_END;
89546 }
89547 return fseek((FILE*)pUserData, offset, whence) == 0;
89548}
89549static ma_bool32 ma_dr_flac__on_tell_stdio(void* pUserData, ma_int64* pCursor)
89550{
89551 FILE* pFileStdio = (FILE*)pUserData;
89552 ma_int64 result;
89553 MA_DR_FLAC_ASSERT(pFileStdio != NULL);
89554 MA_DR_FLAC_ASSERT(pCursor != NULL);
89555#if defined(_WIN32)
89556 #if defined(_MSC_VER) && _MSC_VER > 1200
89557 result = _ftelli64(pFileStdio);
89558 #else
89559 result = ftell(pFileStdio);
89560 #endif
89561#else
89562 result = ftell(pFileStdio);
89563#endif
89564 *pCursor = result;
89565 return MA_TRUE;
89566}
89567MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks)
89568{
89569 ma_dr_flac* pFlac;
89570 FILE* pFile;
89571 if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) {
89572 return NULL;
89573 }
89574 pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, ma_dr_flac__on_tell_stdio, (void*)pFile, pAllocationCallbacks);
89575 if (pFlac == NULL) {
89576 fclose(pFile);
89577 return NULL;
89578 }
89579 return pFlac;
89580}
89581#ifndef MA_DR_FLAC_NO_WCHAR
89582MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks)
89583{
89584 ma_dr_flac* pFlac;
89585 FILE* pFile;
89586 if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) {
89587 return NULL;
89588 }
89589 pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, ma_dr_flac__on_tell_stdio, (void*)pFile, pAllocationCallbacks);
89590 if (pFlac == NULL) {
89591 fclose(pFile);
89592 return NULL;
89593 }
89594 return pFlac;
89595}
89596#endif
89597MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
89598{
89599 ma_dr_flac* pFlac;
89600 FILE* pFile;
89601 if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) {
89602 return NULL;
89603 }
89604 pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, ma_dr_flac__on_tell_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
89605 if (pFlac == NULL) {
89606 fclose(pFile);
89607 return pFlac;
89608 }
89609 return pFlac;
89610}
89611#ifndef MA_DR_FLAC_NO_WCHAR
89612MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
89613{
89614 ma_dr_flac* pFlac;
89615 FILE* pFile;
89616 if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) {
89617 return NULL;
89618 }
89619 pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, ma_dr_flac__on_tell_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
89620 if (pFlac == NULL) {
89621 fclose(pFile);
89622 return pFlac;
89623 }
89624 return pFlac;
89625}
89626#endif
89627#endif
89628static size_t ma_dr_flac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)
89629{
89630 ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData;
89631 size_t bytesRemaining;
89632 MA_DR_FLAC_ASSERT(memoryStream != NULL);
89633 MA_DR_FLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos);
89634 bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos;
89635 if (bytesToRead > bytesRemaining) {
89636 bytesToRead = bytesRemaining;
89637 }
89638 if (bytesToRead > 0) {
89639 MA_DR_FLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead);
89640 memoryStream->currentReadPos += bytesToRead;
89641 }
89642 return bytesToRead;
89643}
89644static ma_bool32 ma_dr_flac__on_seek_memory(void* pUserData, int offset, ma_dr_flac_seek_origin origin)
89645{
89646 ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData;
89647 ma_int64 newCursor;
89648 MA_DR_FLAC_ASSERT(memoryStream != NULL);
89649 newCursor = memoryStream->currentReadPos;
89650 if (origin == MA_DR_FLAC_SEEK_SET) {
89651 newCursor = 0;
89652 } else if (origin == MA_DR_FLAC_SEEK_CUR) {
89653 newCursor = (ma_int64)memoryStream->currentReadPos;
89654 } else if (origin == MA_DR_FLAC_SEEK_END) {
89655 newCursor = (ma_int64)memoryStream->dataSize;
89656 } else {
89657 MA_DR_FLAC_ASSERT(!"Invalid seek origin");
89658 return MA_FALSE;
89659 }
89660 newCursor += offset;
89661 if (newCursor < 0) {
89662 return MA_FALSE;
89663 }
89664 if ((size_t)newCursor > memoryStream->dataSize) {
89665 return MA_FALSE;
89666 }
89667 memoryStream->currentReadPos = (size_t)newCursor;
89668 return MA_TRUE;
89669}
89670static ma_bool32 ma_dr_flac__on_tell_memory(void* pUserData, ma_int64* pCursor)
89671{
89672 ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData;
89673 MA_DR_FLAC_ASSERT(memoryStream != NULL);
89674 MA_DR_FLAC_ASSERT(pCursor != NULL);
89675 *pCursor = (ma_int64)memoryStream->currentReadPos;
89676 return MA_TRUE;
89677}
89678MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks)
89679{
89680 ma_dr_flac__memory_stream memoryStream;
89681 ma_dr_flac* pFlac;
89682 memoryStream.data = (const ma_uint8*)pData;
89683 memoryStream.dataSize = dataSize;
89684 memoryStream.currentReadPos = 0;
89685 pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, ma_dr_flac__on_tell_memory, &memoryStream, pAllocationCallbacks);
89686 if (pFlac == NULL) {
89687 return NULL;
89688 }
89689 pFlac->memoryStream = memoryStream;
89690#ifndef MA_DR_FLAC_NO_OGG
89691 if (pFlac->container == ma_dr_flac_container_ogg)
89692 {
89693 ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs;
89694 oggbs->pUserData = &pFlac->memoryStream;
89695 }
89696 else
89697#endif
89698 {
89699 pFlac->bs.pUserData = &pFlac->memoryStream;
89700 }
89701 return pFlac;
89702}
89703MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
89704{
89705 ma_dr_flac__memory_stream memoryStream;
89706 ma_dr_flac* pFlac;
89707 memoryStream.data = (const ma_uint8*)pData;
89708 memoryStream.dataSize = dataSize;
89709 memoryStream.currentReadPos = 0;
89710 pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, ma_dr_flac__on_tell_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks);
89711 if (pFlac == NULL) {
89712 return NULL;
89713 }
89714 pFlac->memoryStream = memoryStream;
89715#ifndef MA_DR_FLAC_NO_OGG
89716 if (pFlac->container == ma_dr_flac_container_ogg)
89717 {
89718 ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs;
89719 oggbs->pUserData = &pFlac->memoryStream;
89720 }
89721 else
89722#endif
89723 {
89724 pFlac->bs.pUserData = &pFlac->memoryStream;
89725 }
89726 return pFlac;
89727}
89728MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
89729{
89730 return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onTell, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
89731}
89732MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
89733{
89734 return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onTell, NULL, container, pUserData, pUserData, pAllocationCallbacks);
89735}
89736MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
89737{
89738 return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onTell, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
89739}
89740MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
89741{
89742 return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onTell, onMeta, container, pUserData, pUserData, pAllocationCallbacks);
89743}
89744MA_API void ma_dr_flac_close(ma_dr_flac* pFlac)
89745{
89746 if (pFlac == NULL) {
89747 return;
89748 }
89749#ifndef MA_DR_FLAC_NO_STDIO
89750 if (pFlac->bs.onRead == ma_dr_flac__on_read_stdio) {
89751 fclose((FILE*)pFlac->bs.pUserData);
89752 }
89753#ifndef MA_DR_FLAC_NO_OGG
89754 if (pFlac->container == ma_dr_flac_container_ogg) {
89755 ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs;
89756 MA_DR_FLAC_ASSERT(pFlac->bs.onRead == ma_dr_flac__on_read_ogg);
89757 if (oggbs->onRead == ma_dr_flac__on_read_stdio) {
89758 fclose((FILE*)oggbs->pUserData);
89759 }
89760 }
89761#endif
89762#endif
89763 ma_dr_flac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks);
89764}
89765#if 0
89766static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
89767{
89768 ma_uint64 i;
89769 for (i = 0; i < frameCount; ++i) {
89770 ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
89771 ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
89772 ma_uint32 right = left - side;
89773 pOutputSamples[i*2+0] = (ma_int32)left;
89774 pOutputSamples[i*2+1] = (ma_int32)right;
89775 }
89776}
89777#endif
89778static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
89779{
89780 ma_uint64 i;
89781 ma_uint64 frameCount4 = frameCount >> 2;
89782 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
89783 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
89784 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89785 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89786 for (i = 0; i < frameCount4; ++i) {
89787 ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
89788 ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
89789 ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
89790 ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
89791 ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
89792 ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
89793 ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
89794 ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
89795 ma_uint32 right0 = left0 - side0;
89796 ma_uint32 right1 = left1 - side1;
89797 ma_uint32 right2 = left2 - side2;
89798 ma_uint32 right3 = left3 - side3;
89799 pOutputSamples[i*8+0] = (ma_int32)left0;
89800 pOutputSamples[i*8+1] = (ma_int32)right0;
89801 pOutputSamples[i*8+2] = (ma_int32)left1;
89802 pOutputSamples[i*8+3] = (ma_int32)right1;
89803 pOutputSamples[i*8+4] = (ma_int32)left2;
89804 pOutputSamples[i*8+5] = (ma_int32)right2;
89805 pOutputSamples[i*8+6] = (ma_int32)left3;
89806 pOutputSamples[i*8+7] = (ma_int32)right3;
89807 }
89808 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89809 ma_uint32 left = pInputSamples0U32[i] << shift0;
89810 ma_uint32 side = pInputSamples1U32[i] << shift1;
89811 ma_uint32 right = left - side;
89812 pOutputSamples[i*2+0] = (ma_int32)left;
89813 pOutputSamples[i*2+1] = (ma_int32)right;
89814 }
89815}
89816#if defined(MA_DR_FLAC_SUPPORT_SSE2)
89817static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
89818{
89819 ma_uint64 i;
89820 ma_uint64 frameCount4 = frameCount >> 2;
89821 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
89822 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
89823 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89824 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89825 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
89826 for (i = 0; i < frameCount4; ++i) {
89827 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
89828 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
89829 __m128i right = _mm_sub_epi32(left, side);
89830 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
89831 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
89832 }
89833 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89834 ma_uint32 left = pInputSamples0U32[i] << shift0;
89835 ma_uint32 side = pInputSamples1U32[i] << shift1;
89836 ma_uint32 right = left - side;
89837 pOutputSamples[i*2+0] = (ma_int32)left;
89838 pOutputSamples[i*2+1] = (ma_int32)right;
89839 }
89840}
89841#endif
89842#if defined(MA_DR_FLAC_SUPPORT_NEON)
89843static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
89844{
89845 ma_uint64 i;
89846 ma_uint64 frameCount4 = frameCount >> 2;
89847 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
89848 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
89849 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89850 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89851 int32x4_t shift0_4;
89852 int32x4_t shift1_4;
89853 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
89854 shift0_4 = vdupq_n_s32(shift0);
89855 shift1_4 = vdupq_n_s32(shift1);
89856 for (i = 0; i < frameCount4; ++i) {
89857 uint32x4_t left;
89858 uint32x4_t side;
89859 uint32x4_t right;
89860 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
89861 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
89862 right = vsubq_u32(left, side);
89863 ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right));
89864 }
89865 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89866 ma_uint32 left = pInputSamples0U32[i] << shift0;
89867 ma_uint32 side = pInputSamples1U32[i] << shift1;
89868 ma_uint32 right = left - side;
89869 pOutputSamples[i*2+0] = (ma_int32)left;
89870 pOutputSamples[i*2+1] = (ma_int32)right;
89871 }
89872}
89873#endif
89874static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
89875{
89876#if defined(MA_DR_FLAC_SUPPORT_SSE2)
89877 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
89878 ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89879 } else
89880#elif defined(MA_DR_FLAC_SUPPORT_NEON)
89881 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
89882 ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89883 } else
89884#endif
89885 {
89886#if 0
89887 ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89888#else
89889 ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
89890#endif
89891 }
89892}
89893#if 0
89894static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
89895{
89896 ma_uint64 i;
89897 for (i = 0; i < frameCount; ++i) {
89898 ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
89899 ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
89900 ma_uint32 left = right + side;
89901 pOutputSamples[i*2+0] = (ma_int32)left;
89902 pOutputSamples[i*2+1] = (ma_int32)right;
89903 }
89904}
89905#endif
89906static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
89907{
89908 ma_uint64 i;
89909 ma_uint64 frameCount4 = frameCount >> 2;
89910 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
89911 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
89912 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89913 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89914 for (i = 0; i < frameCount4; ++i) {
89915 ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
89916 ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
89917 ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
89918 ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
89919 ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
89920 ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
89921 ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
89922 ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
89923 ma_uint32 left0 = right0 + side0;
89924 ma_uint32 left1 = right1 + side1;
89925 ma_uint32 left2 = right2 + side2;
89926 ma_uint32 left3 = right3 + side3;
89927 pOutputSamples[i*8+0] = (ma_int32)left0;
89928 pOutputSamples[i*8+1] = (ma_int32)right0;
89929 pOutputSamples[i*8+2] = (ma_int32)left1;
89930 pOutputSamples[i*8+3] = (ma_int32)right1;
89931 pOutputSamples[i*8+4] = (ma_int32)left2;
89932 pOutputSamples[i*8+5] = (ma_int32)right2;
89933 pOutputSamples[i*8+6] = (ma_int32)left3;
89934 pOutputSamples[i*8+7] = (ma_int32)right3;
89935 }
89936 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89937 ma_uint32 side = pInputSamples0U32[i] << shift0;
89938 ma_uint32 right = pInputSamples1U32[i] << shift1;
89939 ma_uint32 left = right + side;
89940 pOutputSamples[i*2+0] = (ma_int32)left;
89941 pOutputSamples[i*2+1] = (ma_int32)right;
89942 }
89943}
89944#if defined(MA_DR_FLAC_SUPPORT_SSE2)
89945static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
89946{
89947 ma_uint64 i;
89948 ma_uint64 frameCount4 = frameCount >> 2;
89949 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
89950 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
89951 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89952 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89953 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
89954 for (i = 0; i < frameCount4; ++i) {
89955 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
89956 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
89957 __m128i left = _mm_add_epi32(right, side);
89958 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
89959 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
89960 }
89961 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89962 ma_uint32 side = pInputSamples0U32[i] << shift0;
89963 ma_uint32 right = pInputSamples1U32[i] << shift1;
89964 ma_uint32 left = right + side;
89965 pOutputSamples[i*2+0] = (ma_int32)left;
89966 pOutputSamples[i*2+1] = (ma_int32)right;
89967 }
89968}
89969#endif
89970#if defined(MA_DR_FLAC_SUPPORT_NEON)
89971static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
89972{
89973 ma_uint64 i;
89974 ma_uint64 frameCount4 = frameCount >> 2;
89975 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
89976 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
89977 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
89978 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
89979 int32x4_t shift0_4;
89980 int32x4_t shift1_4;
89981 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
89982 shift0_4 = vdupq_n_s32(shift0);
89983 shift1_4 = vdupq_n_s32(shift1);
89984 for (i = 0; i < frameCount4; ++i) {
89985 uint32x4_t side;
89986 uint32x4_t right;
89987 uint32x4_t left;
89988 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
89989 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
89990 left = vaddq_u32(right, side);
89991 ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right));
89992 }
89993 for (i = (frameCount4 << 2); i < frameCount; ++i) {
89994 ma_uint32 side = pInputSamples0U32[i] << shift0;
89995 ma_uint32 right = pInputSamples1U32[i] << shift1;
89996 ma_uint32 left = right + side;
89997 pOutputSamples[i*2+0] = (ma_int32)left;
89998 pOutputSamples[i*2+1] = (ma_int32)right;
89999 }
90000}
90001#endif
90002static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
90003{
90004#if defined(MA_DR_FLAC_SUPPORT_SSE2)
90005 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
90006 ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90007 } else
90008#elif defined(MA_DR_FLAC_SUPPORT_NEON)
90009 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
90010 ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90011 } else
90012#endif
90013 {
90014#if 0
90015 ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90016#else
90017 ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90018#endif
90019 }
90020}
90021#if 0
90022static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
90023{
90024 for (ma_uint64 i = 0; i < frameCount; ++i) {
90025 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90026 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90027 mid = (mid << 1) | (side & 0x01);
90028 pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample);
90029 pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample);
90030 }
90031}
90032#endif
90033static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
90034{
90035 ma_uint64 i;
90036 ma_uint64 frameCount4 = frameCount >> 2;
90037 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
90038 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
90039 ma_int32 shift = unusedBitsPerSample;
90040 if (shift > 0) {
90041 shift -= 1;
90042 for (i = 0; i < frameCount4; ++i) {
90043 ma_uint32 temp0L;
90044 ma_uint32 temp1L;
90045 ma_uint32 temp2L;
90046 ma_uint32 temp3L;
90047 ma_uint32 temp0R;
90048 ma_uint32 temp1R;
90049 ma_uint32 temp2R;
90050 ma_uint32 temp3R;
90051 ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90052 ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90053 ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90054 ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90055 ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90056 ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90057 ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90058 ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90059 mid0 = (mid0 << 1) | (side0 & 0x01);
90060 mid1 = (mid1 << 1) | (side1 & 0x01);
90061 mid2 = (mid2 << 1) | (side2 & 0x01);
90062 mid3 = (mid3 << 1) | (side3 & 0x01);
90063 temp0L = (mid0 + side0) << shift;
90064 temp1L = (mid1 + side1) << shift;
90065 temp2L = (mid2 + side2) << shift;
90066 temp3L = (mid3 + side3) << shift;
90067 temp0R = (mid0 - side0) << shift;
90068 temp1R = (mid1 - side1) << shift;
90069 temp2R = (mid2 - side2) << shift;
90070 temp3R = (mid3 - side3) << shift;
90071 pOutputSamples[i*8+0] = (ma_int32)temp0L;
90072 pOutputSamples[i*8+1] = (ma_int32)temp0R;
90073 pOutputSamples[i*8+2] = (ma_int32)temp1L;
90074 pOutputSamples[i*8+3] = (ma_int32)temp1R;
90075 pOutputSamples[i*8+4] = (ma_int32)temp2L;
90076 pOutputSamples[i*8+5] = (ma_int32)temp2R;
90077 pOutputSamples[i*8+6] = (ma_int32)temp3L;
90078 pOutputSamples[i*8+7] = (ma_int32)temp3R;
90079 }
90080 } else {
90081 for (i = 0; i < frameCount4; ++i) {
90082 ma_uint32 temp0L;
90083 ma_uint32 temp1L;
90084 ma_uint32 temp2L;
90085 ma_uint32 temp3L;
90086 ma_uint32 temp0R;
90087 ma_uint32 temp1R;
90088 ma_uint32 temp2R;
90089 ma_uint32 temp3R;
90090 ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90091 ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90092 ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90093 ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90094 ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90095 ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90096 ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90097 ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90098 mid0 = (mid0 << 1) | (side0 & 0x01);
90099 mid1 = (mid1 << 1) | (side1 & 0x01);
90100 mid2 = (mid2 << 1) | (side2 & 0x01);
90101 mid3 = (mid3 << 1) | (side3 & 0x01);
90102 temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1);
90103 temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1);
90104 temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1);
90105 temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1);
90106 temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1);
90107 temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1);
90108 temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1);
90109 temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1);
90110 pOutputSamples[i*8+0] = (ma_int32)temp0L;
90111 pOutputSamples[i*8+1] = (ma_int32)temp0R;
90112 pOutputSamples[i*8+2] = (ma_int32)temp1L;
90113 pOutputSamples[i*8+3] = (ma_int32)temp1R;
90114 pOutputSamples[i*8+4] = (ma_int32)temp2L;
90115 pOutputSamples[i*8+5] = (ma_int32)temp2R;
90116 pOutputSamples[i*8+6] = (ma_int32)temp3L;
90117 pOutputSamples[i*8+7] = (ma_int32)temp3R;
90118 }
90119 }
90120 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90121 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90122 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90123 mid = (mid << 1) | (side & 0x01);
90124 pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample);
90125 pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample);
90126 }
90127}
90128#if defined(MA_DR_FLAC_SUPPORT_SSE2)
90129static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
90130{
90131 ma_uint64 i;
90132 ma_uint64 frameCount4 = frameCount >> 2;
90133 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
90134 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
90135 ma_int32 shift = unusedBitsPerSample;
90136 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
90137 if (shift == 0) {
90138 for (i = 0; i < frameCount4; ++i) {
90139 __m128i mid;
90140 __m128i side;
90141 __m128i left;
90142 __m128i right;
90143 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
90144 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
90145 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
90146 left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
90147 right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
90148 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
90149 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
90150 }
90151 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90152 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90153 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90154 mid = (mid << 1) | (side & 0x01);
90155 pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1;
90156 pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1;
90157 }
90158 } else {
90159 shift -= 1;
90160 for (i = 0; i < frameCount4; ++i) {
90161 __m128i mid;
90162 __m128i side;
90163 __m128i left;
90164 __m128i right;
90165 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
90166 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
90167 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
90168 left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
90169 right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
90170 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
90171 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
90172 }
90173 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90174 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90175 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90176 mid = (mid << 1) | (side & 0x01);
90177 pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift);
90178 pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift);
90179 }
90180 }
90181}
90182#endif
90183#if defined(MA_DR_FLAC_SUPPORT_NEON)
90184static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
90185{
90186 ma_uint64 i;
90187 ma_uint64 frameCount4 = frameCount >> 2;
90188 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
90189 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
90190 ma_int32 shift = unusedBitsPerSample;
90191 int32x4_t wbpsShift0_4;
90192 int32x4_t wbpsShift1_4;
90193 uint32x4_t one4;
90194 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
90195 wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
90196 wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
90197 one4 = vdupq_n_u32(1);
90198 if (shift == 0) {
90199 for (i = 0; i < frameCount4; ++i) {
90200 uint32x4_t mid;
90201 uint32x4_t side;
90202 int32x4_t left;
90203 int32x4_t right;
90204 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
90205 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
90206 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4));
90207 left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
90208 right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
90209 ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
90210 }
90211 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90212 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90213 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90214 mid = (mid << 1) | (side & 0x01);
90215 pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1;
90216 pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1;
90217 }
90218 } else {
90219 int32x4_t shift4;
90220 shift -= 1;
90221 shift4 = vdupq_n_s32(shift);
90222 for (i = 0; i < frameCount4; ++i) {
90223 uint32x4_t mid;
90224 uint32x4_t side;
90225 int32x4_t left;
90226 int32x4_t right;
90227 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
90228 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
90229 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4));
90230 left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
90231 right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
90232 ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
90233 }
90234 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90235 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90236 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90237 mid = (mid << 1) | (side & 0x01);
90238 pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift);
90239 pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift);
90240 }
90241 }
90242}
90243#endif
90244static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
90245{
90246#if defined(MA_DR_FLAC_SUPPORT_SSE2)
90247 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
90248 ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90249 } else
90250#elif defined(MA_DR_FLAC_SUPPORT_NEON)
90251 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
90252 ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90253 } else
90254#endif
90255 {
90256#if 0
90257 ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90258#else
90259 ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90260#endif
90261 }
90262}
90263#if 0
90264static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
90265{
90266 for (ma_uint64 i = 0; i < frameCount; ++i) {
90267 pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample));
90268 pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample));
90269 }
90270}
90271#endif
90272static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
90273{
90274 ma_uint64 i;
90275 ma_uint64 frameCount4 = frameCount >> 2;
90276 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
90277 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
90278 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90279 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90280 for (i = 0; i < frameCount4; ++i) {
90281 ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
90282 ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
90283 ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
90284 ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
90285 ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
90286 ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
90287 ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
90288 ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
90289 pOutputSamples[i*8+0] = (ma_int32)tempL0;
90290 pOutputSamples[i*8+1] = (ma_int32)tempR0;
90291 pOutputSamples[i*8+2] = (ma_int32)tempL1;
90292 pOutputSamples[i*8+3] = (ma_int32)tempR1;
90293 pOutputSamples[i*8+4] = (ma_int32)tempL2;
90294 pOutputSamples[i*8+5] = (ma_int32)tempR2;
90295 pOutputSamples[i*8+6] = (ma_int32)tempL3;
90296 pOutputSamples[i*8+7] = (ma_int32)tempR3;
90297 }
90298 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90299 pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0);
90300 pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1);
90301 }
90302}
90303#if defined(MA_DR_FLAC_SUPPORT_SSE2)
90304static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
90305{
90306 ma_uint64 i;
90307 ma_uint64 frameCount4 = frameCount >> 2;
90308 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
90309 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
90310 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90311 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90312 for (i = 0; i < frameCount4; ++i) {
90313 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
90314 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
90315 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
90316 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
90317 }
90318 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90319 pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0);
90320 pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1);
90321 }
90322}
90323#endif
90324#if defined(MA_DR_FLAC_SUPPORT_NEON)
90325static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
90326{
90327 ma_uint64 i;
90328 ma_uint64 frameCount4 = frameCount >> 2;
90329 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
90330 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
90331 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90332 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90333 int32x4_t shift4_0 = vdupq_n_s32(shift0);
90334 int32x4_t shift4_1 = vdupq_n_s32(shift1);
90335 for (i = 0; i < frameCount4; ++i) {
90336 int32x4_t left;
90337 int32x4_t right;
90338 left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0));
90339 right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1));
90340 ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
90341 }
90342 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90343 pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0);
90344 pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1);
90345 }
90346}
90347#endif
90348static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
90349{
90350#if defined(MA_DR_FLAC_SUPPORT_SSE2)
90351 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
90352 ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90353 } else
90354#elif defined(MA_DR_FLAC_SUPPORT_NEON)
90355 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
90356 ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90357 } else
90358#endif
90359 {
90360#if 0
90361 ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90362#else
90363 ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90364#endif
90365 }
90366}
90367MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut)
90368{
90369 ma_uint64 framesRead;
90370 ma_uint32 unusedBitsPerSample;
90371 if (pFlac == NULL || framesToRead == 0) {
90372 return 0;
90373 }
90374 if (pBufferOut == NULL) {
90375 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead);
90376 }
90377 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32);
90378 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
90379 framesRead = 0;
90380 while (framesToRead > 0) {
90381 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
90382 if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {
90383 break;
90384 }
90385 } else {
90386 unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
90387 ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
90388 ma_uint64 frameCountThisIteration = framesToRead;
90389 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
90390 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
90391 }
90392 if (channelCount == 2) {
90393 const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
90394 const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
90395 switch (pFlac->currentFLACFrame.header.channelAssignment)
90396 {
90397 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
90398 {
90399 ma_dr_flac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
90400 } break;
90401 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
90402 {
90403 ma_dr_flac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
90404 } break;
90405 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
90406 {
90407 ma_dr_flac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
90408 } break;
90409 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
90410 default:
90411 {
90412 ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
90413 } break;
90414 }
90415 } else {
90416 ma_uint64 i;
90417 for (i = 0; i < frameCountThisIteration; ++i) {
90418 unsigned int j;
90419 for (j = 0; j < channelCount; ++j) {
90420 pBufferOut[(i*channelCount)+j] = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
90421 }
90422 }
90423 }
90424 framesRead += frameCountThisIteration;
90425 pBufferOut += frameCountThisIteration * channelCount;
90426 framesToRead -= frameCountThisIteration;
90427 pFlac->currentPCMFrame += frameCountThisIteration;
90428 pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration;
90429 }
90430 }
90431 return framesRead;
90432}
90433#if 0
90434static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90435{
90436 ma_uint64 i;
90437 for (i = 0; i < frameCount; ++i) {
90438 ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
90439 ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
90440 ma_uint32 right = left - side;
90441 left >>= 16;
90442 right >>= 16;
90443 pOutputSamples[i*2+0] = (ma_int16)left;
90444 pOutputSamples[i*2+1] = (ma_int16)right;
90445 }
90446}
90447#endif
90448static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90449{
90450 ma_uint64 i;
90451 ma_uint64 frameCount4 = frameCount >> 2;
90452 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
90453 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
90454 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90455 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90456 for (i = 0; i < frameCount4; ++i) {
90457 ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
90458 ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
90459 ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
90460 ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
90461 ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
90462 ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
90463 ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
90464 ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
90465 ma_uint32 right0 = left0 - side0;
90466 ma_uint32 right1 = left1 - side1;
90467 ma_uint32 right2 = left2 - side2;
90468 ma_uint32 right3 = left3 - side3;
90469 left0 >>= 16;
90470 left1 >>= 16;
90471 left2 >>= 16;
90472 left3 >>= 16;
90473 right0 >>= 16;
90474 right1 >>= 16;
90475 right2 >>= 16;
90476 right3 >>= 16;
90477 pOutputSamples[i*8+0] = (ma_int16)left0;
90478 pOutputSamples[i*8+1] = (ma_int16)right0;
90479 pOutputSamples[i*8+2] = (ma_int16)left1;
90480 pOutputSamples[i*8+3] = (ma_int16)right1;
90481 pOutputSamples[i*8+4] = (ma_int16)left2;
90482 pOutputSamples[i*8+5] = (ma_int16)right2;
90483 pOutputSamples[i*8+6] = (ma_int16)left3;
90484 pOutputSamples[i*8+7] = (ma_int16)right3;
90485 }
90486 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90487 ma_uint32 left = pInputSamples0U32[i] << shift0;
90488 ma_uint32 side = pInputSamples1U32[i] << shift1;
90489 ma_uint32 right = left - side;
90490 left >>= 16;
90491 right >>= 16;
90492 pOutputSamples[i*2+0] = (ma_int16)left;
90493 pOutputSamples[i*2+1] = (ma_int16)right;
90494 }
90495}
90496#if defined(MA_DR_FLAC_SUPPORT_SSE2)
90497static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90498{
90499 ma_uint64 i;
90500 ma_uint64 frameCount4 = frameCount >> 2;
90501 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
90502 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
90503 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90504 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90505 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
90506 for (i = 0; i < frameCount4; ++i) {
90507 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
90508 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
90509 __m128i right = _mm_sub_epi32(left, side);
90510 left = _mm_srai_epi32(left, 16);
90511 right = _mm_srai_epi32(right, 16);
90512 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));
90513 }
90514 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90515 ma_uint32 left = pInputSamples0U32[i] << shift0;
90516 ma_uint32 side = pInputSamples1U32[i] << shift1;
90517 ma_uint32 right = left - side;
90518 left >>= 16;
90519 right >>= 16;
90520 pOutputSamples[i*2+0] = (ma_int16)left;
90521 pOutputSamples[i*2+1] = (ma_int16)right;
90522 }
90523}
90524#endif
90525#if defined(MA_DR_FLAC_SUPPORT_NEON)
90526static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90527{
90528 ma_uint64 i;
90529 ma_uint64 frameCount4 = frameCount >> 2;
90530 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
90531 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
90532 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90533 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90534 int32x4_t shift0_4;
90535 int32x4_t shift1_4;
90536 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
90537 shift0_4 = vdupq_n_s32(shift0);
90538 shift1_4 = vdupq_n_s32(shift1);
90539 for (i = 0; i < frameCount4; ++i) {
90540 uint32x4_t left;
90541 uint32x4_t side;
90542 uint32x4_t right;
90543 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
90544 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
90545 right = vsubq_u32(left, side);
90546 left = vshrq_n_u32(left, 16);
90547 right = vshrq_n_u32(right, 16);
90548 ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right)));
90549 }
90550 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90551 ma_uint32 left = pInputSamples0U32[i] << shift0;
90552 ma_uint32 side = pInputSamples1U32[i] << shift1;
90553 ma_uint32 right = left - side;
90554 left >>= 16;
90555 right >>= 16;
90556 pOutputSamples[i*2+0] = (ma_int16)left;
90557 pOutputSamples[i*2+1] = (ma_int16)right;
90558 }
90559}
90560#endif
90561static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90562{
90563#if defined(MA_DR_FLAC_SUPPORT_SSE2)
90564 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
90565 ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90566 } else
90567#elif defined(MA_DR_FLAC_SUPPORT_NEON)
90568 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
90569 ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90570 } else
90571#endif
90572 {
90573#if 0
90574 ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90575#else
90576 ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90577#endif
90578 }
90579}
90580#if 0
90581static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90582{
90583 ma_uint64 i;
90584 for (i = 0; i < frameCount; ++i) {
90585 ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
90586 ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
90587 ma_uint32 left = right + side;
90588 left >>= 16;
90589 right >>= 16;
90590 pOutputSamples[i*2+0] = (ma_int16)left;
90591 pOutputSamples[i*2+1] = (ma_int16)right;
90592 }
90593}
90594#endif
90595static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90596{
90597 ma_uint64 i;
90598 ma_uint64 frameCount4 = frameCount >> 2;
90599 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
90600 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
90601 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90602 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90603 for (i = 0; i < frameCount4; ++i) {
90604 ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
90605 ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
90606 ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
90607 ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
90608 ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
90609 ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
90610 ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
90611 ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
90612 ma_uint32 left0 = right0 + side0;
90613 ma_uint32 left1 = right1 + side1;
90614 ma_uint32 left2 = right2 + side2;
90615 ma_uint32 left3 = right3 + side3;
90616 left0 >>= 16;
90617 left1 >>= 16;
90618 left2 >>= 16;
90619 left3 >>= 16;
90620 right0 >>= 16;
90621 right1 >>= 16;
90622 right2 >>= 16;
90623 right3 >>= 16;
90624 pOutputSamples[i*8+0] = (ma_int16)left0;
90625 pOutputSamples[i*8+1] = (ma_int16)right0;
90626 pOutputSamples[i*8+2] = (ma_int16)left1;
90627 pOutputSamples[i*8+3] = (ma_int16)right1;
90628 pOutputSamples[i*8+4] = (ma_int16)left2;
90629 pOutputSamples[i*8+5] = (ma_int16)right2;
90630 pOutputSamples[i*8+6] = (ma_int16)left3;
90631 pOutputSamples[i*8+7] = (ma_int16)right3;
90632 }
90633 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90634 ma_uint32 side = pInputSamples0U32[i] << shift0;
90635 ma_uint32 right = pInputSamples1U32[i] << shift1;
90636 ma_uint32 left = right + side;
90637 left >>= 16;
90638 right >>= 16;
90639 pOutputSamples[i*2+0] = (ma_int16)left;
90640 pOutputSamples[i*2+1] = (ma_int16)right;
90641 }
90642}
90643#if defined(MA_DR_FLAC_SUPPORT_SSE2)
90644static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90645{
90646 ma_uint64 i;
90647 ma_uint64 frameCount4 = frameCount >> 2;
90648 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
90649 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
90650 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90651 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90652 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
90653 for (i = 0; i < frameCount4; ++i) {
90654 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
90655 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
90656 __m128i left = _mm_add_epi32(right, side);
90657 left = _mm_srai_epi32(left, 16);
90658 right = _mm_srai_epi32(right, 16);
90659 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));
90660 }
90661 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90662 ma_uint32 side = pInputSamples0U32[i] << shift0;
90663 ma_uint32 right = pInputSamples1U32[i] << shift1;
90664 ma_uint32 left = right + side;
90665 left >>= 16;
90666 right >>= 16;
90667 pOutputSamples[i*2+0] = (ma_int16)left;
90668 pOutputSamples[i*2+1] = (ma_int16)right;
90669 }
90670}
90671#endif
90672#if defined(MA_DR_FLAC_SUPPORT_NEON)
90673static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90674{
90675 ma_uint64 i;
90676 ma_uint64 frameCount4 = frameCount >> 2;
90677 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
90678 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
90679 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90680 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90681 int32x4_t shift0_4;
90682 int32x4_t shift1_4;
90683 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
90684 shift0_4 = vdupq_n_s32(shift0);
90685 shift1_4 = vdupq_n_s32(shift1);
90686 for (i = 0; i < frameCount4; ++i) {
90687 uint32x4_t side;
90688 uint32x4_t right;
90689 uint32x4_t left;
90690 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
90691 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
90692 left = vaddq_u32(right, side);
90693 left = vshrq_n_u32(left, 16);
90694 right = vshrq_n_u32(right, 16);
90695 ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right)));
90696 }
90697 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90698 ma_uint32 side = pInputSamples0U32[i] << shift0;
90699 ma_uint32 right = pInputSamples1U32[i] << shift1;
90700 ma_uint32 left = right + side;
90701 left >>= 16;
90702 right >>= 16;
90703 pOutputSamples[i*2+0] = (ma_int16)left;
90704 pOutputSamples[i*2+1] = (ma_int16)right;
90705 }
90706}
90707#endif
90708static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90709{
90710#if defined(MA_DR_FLAC_SUPPORT_SSE2)
90711 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
90712 ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90713 } else
90714#elif defined(MA_DR_FLAC_SUPPORT_NEON)
90715 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
90716 ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90717 } else
90718#endif
90719 {
90720#if 0
90721 ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90722#else
90723 ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90724#endif
90725 }
90726}
90727#if 0
90728static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90729{
90730 for (ma_uint64 i = 0; i < frameCount; ++i) {
90731 ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90732 ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90733 mid = (mid << 1) | (side & 0x01);
90734 pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16);
90735 pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16);
90736 }
90737}
90738#endif
90739static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90740{
90741 ma_uint64 i;
90742 ma_uint64 frameCount4 = frameCount >> 2;
90743 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
90744 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
90745 ma_uint32 shift = unusedBitsPerSample;
90746 if (shift > 0) {
90747 shift -= 1;
90748 for (i = 0; i < frameCount4; ++i) {
90749 ma_uint32 temp0L;
90750 ma_uint32 temp1L;
90751 ma_uint32 temp2L;
90752 ma_uint32 temp3L;
90753 ma_uint32 temp0R;
90754 ma_uint32 temp1R;
90755 ma_uint32 temp2R;
90756 ma_uint32 temp3R;
90757 ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90758 ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90759 ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90760 ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90761 ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90762 ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90763 ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90764 ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90765 mid0 = (mid0 << 1) | (side0 & 0x01);
90766 mid1 = (mid1 << 1) | (side1 & 0x01);
90767 mid2 = (mid2 << 1) | (side2 & 0x01);
90768 mid3 = (mid3 << 1) | (side3 & 0x01);
90769 temp0L = (mid0 + side0) << shift;
90770 temp1L = (mid1 + side1) << shift;
90771 temp2L = (mid2 + side2) << shift;
90772 temp3L = (mid3 + side3) << shift;
90773 temp0R = (mid0 - side0) << shift;
90774 temp1R = (mid1 - side1) << shift;
90775 temp2R = (mid2 - side2) << shift;
90776 temp3R = (mid3 - side3) << shift;
90777 temp0L >>= 16;
90778 temp1L >>= 16;
90779 temp2L >>= 16;
90780 temp3L >>= 16;
90781 temp0R >>= 16;
90782 temp1R >>= 16;
90783 temp2R >>= 16;
90784 temp3R >>= 16;
90785 pOutputSamples[i*8+0] = (ma_int16)temp0L;
90786 pOutputSamples[i*8+1] = (ma_int16)temp0R;
90787 pOutputSamples[i*8+2] = (ma_int16)temp1L;
90788 pOutputSamples[i*8+3] = (ma_int16)temp1R;
90789 pOutputSamples[i*8+4] = (ma_int16)temp2L;
90790 pOutputSamples[i*8+5] = (ma_int16)temp2R;
90791 pOutputSamples[i*8+6] = (ma_int16)temp3L;
90792 pOutputSamples[i*8+7] = (ma_int16)temp3R;
90793 }
90794 } else {
90795 for (i = 0; i < frameCount4; ++i) {
90796 ma_uint32 temp0L;
90797 ma_uint32 temp1L;
90798 ma_uint32 temp2L;
90799 ma_uint32 temp3L;
90800 ma_uint32 temp0R;
90801 ma_uint32 temp1R;
90802 ma_uint32 temp2R;
90803 ma_uint32 temp3R;
90804 ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90805 ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90806 ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90807 ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90808 ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90809 ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90810 ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90811 ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90812 mid0 = (mid0 << 1) | (side0 & 0x01);
90813 mid1 = (mid1 << 1) | (side1 & 0x01);
90814 mid2 = (mid2 << 1) | (side2 & 0x01);
90815 mid3 = (mid3 << 1) | (side3 & 0x01);
90816 temp0L = ((ma_int32)(mid0 + side0) >> 1);
90817 temp1L = ((ma_int32)(mid1 + side1) >> 1);
90818 temp2L = ((ma_int32)(mid2 + side2) >> 1);
90819 temp3L = ((ma_int32)(mid3 + side3) >> 1);
90820 temp0R = ((ma_int32)(mid0 - side0) >> 1);
90821 temp1R = ((ma_int32)(mid1 - side1) >> 1);
90822 temp2R = ((ma_int32)(mid2 - side2) >> 1);
90823 temp3R = ((ma_int32)(mid3 - side3) >> 1);
90824 temp0L >>= 16;
90825 temp1L >>= 16;
90826 temp2L >>= 16;
90827 temp3L >>= 16;
90828 temp0R >>= 16;
90829 temp1R >>= 16;
90830 temp2R >>= 16;
90831 temp3R >>= 16;
90832 pOutputSamples[i*8+0] = (ma_int16)temp0L;
90833 pOutputSamples[i*8+1] = (ma_int16)temp0R;
90834 pOutputSamples[i*8+2] = (ma_int16)temp1L;
90835 pOutputSamples[i*8+3] = (ma_int16)temp1R;
90836 pOutputSamples[i*8+4] = (ma_int16)temp2L;
90837 pOutputSamples[i*8+5] = (ma_int16)temp2R;
90838 pOutputSamples[i*8+6] = (ma_int16)temp3L;
90839 pOutputSamples[i*8+7] = (ma_int16)temp3R;
90840 }
90841 }
90842 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90843 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90844 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90845 mid = (mid << 1) | (side & 0x01);
90846 pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16);
90847 pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16);
90848 }
90849}
90850#if defined(MA_DR_FLAC_SUPPORT_SSE2)
90851static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90852{
90853 ma_uint64 i;
90854 ma_uint64 frameCount4 = frameCount >> 2;
90855 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
90856 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
90857 ma_uint32 shift = unusedBitsPerSample;
90858 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
90859 if (shift == 0) {
90860 for (i = 0; i < frameCount4; ++i) {
90861 __m128i mid;
90862 __m128i side;
90863 __m128i left;
90864 __m128i right;
90865 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
90866 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
90867 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
90868 left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
90869 right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
90870 left = _mm_srai_epi32(left, 16);
90871 right = _mm_srai_epi32(right, 16);
90872 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));
90873 }
90874 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90875 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90876 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90877 mid = (mid << 1) | (side & 0x01);
90878 pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16);
90879 pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16);
90880 }
90881 } else {
90882 shift -= 1;
90883 for (i = 0; i < frameCount4; ++i) {
90884 __m128i mid;
90885 __m128i side;
90886 __m128i left;
90887 __m128i right;
90888 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
90889 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
90890 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
90891 left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
90892 right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
90893 left = _mm_srai_epi32(left, 16);
90894 right = _mm_srai_epi32(right, 16);
90895 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));
90896 }
90897 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90898 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90899 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90900 mid = (mid << 1) | (side & 0x01);
90901 pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16);
90902 pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16);
90903 }
90904 }
90905}
90906#endif
90907#if defined(MA_DR_FLAC_SUPPORT_NEON)
90908static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90909{
90910 ma_uint64 i;
90911 ma_uint64 frameCount4 = frameCount >> 2;
90912 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
90913 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
90914 ma_uint32 shift = unusedBitsPerSample;
90915 int32x4_t wbpsShift0_4;
90916 int32x4_t wbpsShift1_4;
90917 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
90918 wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
90919 wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
90920 if (shift == 0) {
90921 for (i = 0; i < frameCount4; ++i) {
90922 uint32x4_t mid;
90923 uint32x4_t side;
90924 int32x4_t left;
90925 int32x4_t right;
90926 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
90927 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
90928 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
90929 left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
90930 right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
90931 left = vshrq_n_s32(left, 16);
90932 right = vshrq_n_s32(right, 16);
90933 ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
90934 }
90935 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90936 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90937 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90938 mid = (mid << 1) | (side & 0x01);
90939 pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16);
90940 pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16);
90941 }
90942 } else {
90943 int32x4_t shift4;
90944 shift -= 1;
90945 shift4 = vdupq_n_s32(shift);
90946 for (i = 0; i < frameCount4; ++i) {
90947 uint32x4_t mid;
90948 uint32x4_t side;
90949 int32x4_t left;
90950 int32x4_t right;
90951 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
90952 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
90953 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
90954 left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
90955 right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
90956 left = vshrq_n_s32(left, 16);
90957 right = vshrq_n_s32(right, 16);
90958 ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
90959 }
90960 for (i = (frameCount4 << 2); i < frameCount; ++i) {
90961 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
90962 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
90963 mid = (mid << 1) | (side & 0x01);
90964 pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16);
90965 pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16);
90966 }
90967 }
90968}
90969#endif
90970static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90971{
90972#if defined(MA_DR_FLAC_SUPPORT_SSE2)
90973 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
90974 ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90975 } else
90976#elif defined(MA_DR_FLAC_SUPPORT_NEON)
90977 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
90978 ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90979 } else
90980#endif
90981 {
90982#if 0
90983 ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90984#else
90985 ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
90986#endif
90987 }
90988}
90989#if 0
90990static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90991{
90992 for (ma_uint64 i = 0; i < frameCount; ++i) {
90993 pOutputSamples[i*2+0] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16);
90994 pOutputSamples[i*2+1] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16);
90995 }
90996}
90997#endif
90998static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
90999{
91000 ma_uint64 i;
91001 ma_uint64 frameCount4 = frameCount >> 2;
91002 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
91003 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
91004 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91005 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91006 for (i = 0; i < frameCount4; ++i) {
91007 ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
91008 ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
91009 ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
91010 ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
91011 ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
91012 ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
91013 ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
91014 ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
91015 tempL0 >>= 16;
91016 tempL1 >>= 16;
91017 tempL2 >>= 16;
91018 tempL3 >>= 16;
91019 tempR0 >>= 16;
91020 tempR1 >>= 16;
91021 tempR2 >>= 16;
91022 tempR3 >>= 16;
91023 pOutputSamples[i*8+0] = (ma_int16)tempL0;
91024 pOutputSamples[i*8+1] = (ma_int16)tempR0;
91025 pOutputSamples[i*8+2] = (ma_int16)tempL1;
91026 pOutputSamples[i*8+3] = (ma_int16)tempR1;
91027 pOutputSamples[i*8+4] = (ma_int16)tempL2;
91028 pOutputSamples[i*8+5] = (ma_int16)tempR2;
91029 pOutputSamples[i*8+6] = (ma_int16)tempL3;
91030 pOutputSamples[i*8+7] = (ma_int16)tempR3;
91031 }
91032 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91033 pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16);
91034 pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16);
91035 }
91036}
91037#if defined(MA_DR_FLAC_SUPPORT_SSE2)
91038static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
91039{
91040 ma_uint64 i;
91041 ma_uint64 frameCount4 = frameCount >> 2;
91042 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
91043 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
91044 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91045 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91046 for (i = 0; i < frameCount4; ++i) {
91047 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
91048 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
91049 left = _mm_srai_epi32(left, 16);
91050 right = _mm_srai_epi32(right, 16);
91051 _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));
91052 }
91053 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91054 pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16);
91055 pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16);
91056 }
91057}
91058#endif
91059#if defined(MA_DR_FLAC_SUPPORT_NEON)
91060static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
91061{
91062 ma_uint64 i;
91063 ma_uint64 frameCount4 = frameCount >> 2;
91064 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
91065 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
91066 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91067 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91068 int32x4_t shift0_4 = vdupq_n_s32(shift0);
91069 int32x4_t shift1_4 = vdupq_n_s32(shift1);
91070 for (i = 0; i < frameCount4; ++i) {
91071 int32x4_t left;
91072 int32x4_t right;
91073 left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));
91074 right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));
91075 left = vshrq_n_s32(left, 16);
91076 right = vshrq_n_s32(right, 16);
91077 ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
91078 }
91079 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91080 pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16);
91081 pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16);
91082 }
91083}
91084#endif
91085static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
91086{
91087#if defined(MA_DR_FLAC_SUPPORT_SSE2)
91088 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
91089 ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91090 } else
91091#elif defined(MA_DR_FLAC_SUPPORT_NEON)
91092 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
91093 ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91094 } else
91095#endif
91096 {
91097#if 0
91098 ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91099#else
91100 ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91101#endif
91102 }
91103}
91104MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut)
91105{
91106 ma_uint64 framesRead;
91107 ma_uint32 unusedBitsPerSample;
91108 if (pFlac == NULL || framesToRead == 0) {
91109 return 0;
91110 }
91111 if (pBufferOut == NULL) {
91112 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead);
91113 }
91114 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32);
91115 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
91116 framesRead = 0;
91117 while (framesToRead > 0) {
91118 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
91119 if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {
91120 break;
91121 }
91122 } else {
91123 unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
91124 ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
91125 ma_uint64 frameCountThisIteration = framesToRead;
91126 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
91127 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
91128 }
91129 if (channelCount == 2) {
91130 const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
91131 const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
91132 switch (pFlac->currentFLACFrame.header.channelAssignment)
91133 {
91134 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
91135 {
91136 ma_dr_flac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
91137 } break;
91138 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
91139 {
91140 ma_dr_flac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
91141 } break;
91142 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
91143 {
91144 ma_dr_flac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
91145 } break;
91146 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
91147 default:
91148 {
91149 ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
91150 } break;
91151 }
91152 } else {
91153 ma_uint64 i;
91154 for (i = 0; i < frameCountThisIteration; ++i) {
91155 unsigned int j;
91156 for (j = 0; j < channelCount; ++j) {
91157 ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
91158 pBufferOut[(i*channelCount)+j] = (ma_int16)(sampleS32 >> 16);
91159 }
91160 }
91161 }
91162 framesRead += frameCountThisIteration;
91163 pBufferOut += frameCountThisIteration * channelCount;
91164 framesToRead -= frameCountThisIteration;
91165 pFlac->currentPCMFrame += frameCountThisIteration;
91166 pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration;
91167 }
91168 }
91169 return framesRead;
91170}
91171#if 0
91172static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91173{
91174 ma_uint64 i;
91175 for (i = 0; i < frameCount; ++i) {
91176 ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
91177 ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
91178 ma_uint32 right = left - side;
91179 pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0);
91180 pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0);
91181 }
91182}
91183#endif
91184static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91185{
91186 ma_uint64 i;
91187 ma_uint64 frameCount4 = frameCount >> 2;
91188 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
91189 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
91190 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91191 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91192 float factor = 1 / 2147483648.0;
91193 for (i = 0; i < frameCount4; ++i) {
91194 ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
91195 ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
91196 ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
91197 ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
91198 ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
91199 ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
91200 ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
91201 ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
91202 ma_uint32 right0 = left0 - side0;
91203 ma_uint32 right1 = left1 - side1;
91204 ma_uint32 right2 = left2 - side2;
91205 ma_uint32 right3 = left3 - side3;
91206 pOutputSamples[i*8+0] = (ma_int32)left0 * factor;
91207 pOutputSamples[i*8+1] = (ma_int32)right0 * factor;
91208 pOutputSamples[i*8+2] = (ma_int32)left1 * factor;
91209 pOutputSamples[i*8+3] = (ma_int32)right1 * factor;
91210 pOutputSamples[i*8+4] = (ma_int32)left2 * factor;
91211 pOutputSamples[i*8+5] = (ma_int32)right2 * factor;
91212 pOutputSamples[i*8+6] = (ma_int32)left3 * factor;
91213 pOutputSamples[i*8+7] = (ma_int32)right3 * factor;
91214 }
91215 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91216 ma_uint32 left = pInputSamples0U32[i] << shift0;
91217 ma_uint32 side = pInputSamples1U32[i] << shift1;
91218 ma_uint32 right = left - side;
91219 pOutputSamples[i*2+0] = (ma_int32)left * factor;
91220 pOutputSamples[i*2+1] = (ma_int32)right * factor;
91221 }
91222}
91223#if defined(MA_DR_FLAC_SUPPORT_SSE2)
91224static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91225{
91226 ma_uint64 i;
91227 ma_uint64 frameCount4 = frameCount >> 2;
91228 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
91229 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
91230 ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
91231 ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
91232 __m128 factor;
91233 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
91234 factor = _mm_set1_ps(1.0f / 8388608.0f);
91235 for (i = 0; i < frameCount4; ++i) {
91236 __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
91237 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
91238 __m128i right = _mm_sub_epi32(left, side);
91239 __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
91240 __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
91241 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
91242 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
91243 }
91244 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91245 ma_uint32 left = pInputSamples0U32[i] << shift0;
91246 ma_uint32 side = pInputSamples1U32[i] << shift1;
91247 ma_uint32 right = left - side;
91248 pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f;
91249 pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f;
91250 }
91251}
91252#endif
91253#if defined(MA_DR_FLAC_SUPPORT_NEON)
91254static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91255{
91256 ma_uint64 i;
91257 ma_uint64 frameCount4 = frameCount >> 2;
91258 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
91259 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
91260 ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
91261 ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
91262 float32x4_t factor4;
91263 int32x4_t shift0_4;
91264 int32x4_t shift1_4;
91265 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
91266 factor4 = vdupq_n_f32(1.0f / 8388608.0f);
91267 shift0_4 = vdupq_n_s32(shift0);
91268 shift1_4 = vdupq_n_s32(shift1);
91269 for (i = 0; i < frameCount4; ++i) {
91270 uint32x4_t left;
91271 uint32x4_t side;
91272 uint32x4_t right;
91273 float32x4_t leftf;
91274 float32x4_t rightf;
91275 left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
91276 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
91277 right = vsubq_u32(left, side);
91278 leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4);
91279 rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4);
91280 ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
91281 }
91282 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91283 ma_uint32 left = pInputSamples0U32[i] << shift0;
91284 ma_uint32 side = pInputSamples1U32[i] << shift1;
91285 ma_uint32 right = left - side;
91286 pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f;
91287 pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f;
91288 }
91289}
91290#endif
91291static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91292{
91293#if defined(MA_DR_FLAC_SUPPORT_SSE2)
91294 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
91295 ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91296 } else
91297#elif defined(MA_DR_FLAC_SUPPORT_NEON)
91298 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
91299 ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91300 } else
91301#endif
91302 {
91303#if 0
91304 ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91305#else
91306 ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91307#endif
91308 }
91309}
91310#if 0
91311static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91312{
91313 ma_uint64 i;
91314 for (i = 0; i < frameCount; ++i) {
91315 ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
91316 ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
91317 ma_uint32 left = right + side;
91318 pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0);
91319 pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0);
91320 }
91321}
91322#endif
91323static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91324{
91325 ma_uint64 i;
91326 ma_uint64 frameCount4 = frameCount >> 2;
91327 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
91328 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
91329 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91330 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91331 float factor = 1 / 2147483648.0;
91332 for (i = 0; i < frameCount4; ++i) {
91333 ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
91334 ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
91335 ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
91336 ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
91337 ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
91338 ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
91339 ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
91340 ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
91341 ma_uint32 left0 = right0 + side0;
91342 ma_uint32 left1 = right1 + side1;
91343 ma_uint32 left2 = right2 + side2;
91344 ma_uint32 left3 = right3 + side3;
91345 pOutputSamples[i*8+0] = (ma_int32)left0 * factor;
91346 pOutputSamples[i*8+1] = (ma_int32)right0 * factor;
91347 pOutputSamples[i*8+2] = (ma_int32)left1 * factor;
91348 pOutputSamples[i*8+3] = (ma_int32)right1 * factor;
91349 pOutputSamples[i*8+4] = (ma_int32)left2 * factor;
91350 pOutputSamples[i*8+5] = (ma_int32)right2 * factor;
91351 pOutputSamples[i*8+6] = (ma_int32)left3 * factor;
91352 pOutputSamples[i*8+7] = (ma_int32)right3 * factor;
91353 }
91354 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91355 ma_uint32 side = pInputSamples0U32[i] << shift0;
91356 ma_uint32 right = pInputSamples1U32[i] << shift1;
91357 ma_uint32 left = right + side;
91358 pOutputSamples[i*2+0] = (ma_int32)left * factor;
91359 pOutputSamples[i*2+1] = (ma_int32)right * factor;
91360 }
91361}
91362#if defined(MA_DR_FLAC_SUPPORT_SSE2)
91363static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91364{
91365 ma_uint64 i;
91366 ma_uint64 frameCount4 = frameCount >> 2;
91367 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
91368 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
91369 ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
91370 ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
91371 __m128 factor;
91372 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
91373 factor = _mm_set1_ps(1.0f / 8388608.0f);
91374 for (i = 0; i < frameCount4; ++i) {
91375 __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
91376 __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
91377 __m128i left = _mm_add_epi32(right, side);
91378 __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
91379 __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
91380 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
91381 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
91382 }
91383 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91384 ma_uint32 side = pInputSamples0U32[i] << shift0;
91385 ma_uint32 right = pInputSamples1U32[i] << shift1;
91386 ma_uint32 left = right + side;
91387 pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f;
91388 pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f;
91389 }
91390}
91391#endif
91392#if defined(MA_DR_FLAC_SUPPORT_NEON)
91393static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91394{
91395 ma_uint64 i;
91396 ma_uint64 frameCount4 = frameCount >> 2;
91397 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
91398 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
91399 ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
91400 ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
91401 float32x4_t factor4;
91402 int32x4_t shift0_4;
91403 int32x4_t shift1_4;
91404 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
91405 factor4 = vdupq_n_f32(1.0f / 8388608.0f);
91406 shift0_4 = vdupq_n_s32(shift0);
91407 shift1_4 = vdupq_n_s32(shift1);
91408 for (i = 0; i < frameCount4; ++i) {
91409 uint32x4_t side;
91410 uint32x4_t right;
91411 uint32x4_t left;
91412 float32x4_t leftf;
91413 float32x4_t rightf;
91414 side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
91415 right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
91416 left = vaddq_u32(right, side);
91417 leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4);
91418 rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4);
91419 ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
91420 }
91421 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91422 ma_uint32 side = pInputSamples0U32[i] << shift0;
91423 ma_uint32 right = pInputSamples1U32[i] << shift1;
91424 ma_uint32 left = right + side;
91425 pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f;
91426 pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f;
91427 }
91428}
91429#endif
91430static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91431{
91432#if defined(MA_DR_FLAC_SUPPORT_SSE2)
91433 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
91434 ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91435 } else
91436#elif defined(MA_DR_FLAC_SUPPORT_NEON)
91437 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
91438 ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91439 } else
91440#endif
91441 {
91442#if 0
91443 ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91444#else
91445 ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91446#endif
91447 }
91448}
91449#if 0
91450static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91451{
91452 for (ma_uint64 i = 0; i < frameCount; ++i) {
91453 ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91454 ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91455 mid = (mid << 1) | (side & 0x01);
91456 pOutputSamples[i*2+0] = (float)((((ma_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
91457 pOutputSamples[i*2+1] = (float)((((ma_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
91458 }
91459}
91460#endif
91461static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91462{
91463 ma_uint64 i;
91464 ma_uint64 frameCount4 = frameCount >> 2;
91465 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
91466 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
91467 ma_uint32 shift = unusedBitsPerSample;
91468 float factor = 1 / 2147483648.0;
91469 if (shift > 0) {
91470 shift -= 1;
91471 for (i = 0; i < frameCount4; ++i) {
91472 ma_uint32 temp0L;
91473 ma_uint32 temp1L;
91474 ma_uint32 temp2L;
91475 ma_uint32 temp3L;
91476 ma_uint32 temp0R;
91477 ma_uint32 temp1R;
91478 ma_uint32 temp2R;
91479 ma_uint32 temp3R;
91480 ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91481 ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91482 ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91483 ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91484 ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91485 ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91486 ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91487 ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91488 mid0 = (mid0 << 1) | (side0 & 0x01);
91489 mid1 = (mid1 << 1) | (side1 & 0x01);
91490 mid2 = (mid2 << 1) | (side2 & 0x01);
91491 mid3 = (mid3 << 1) | (side3 & 0x01);
91492 temp0L = (mid0 + side0) << shift;
91493 temp1L = (mid1 + side1) << shift;
91494 temp2L = (mid2 + side2) << shift;
91495 temp3L = (mid3 + side3) << shift;
91496 temp0R = (mid0 - side0) << shift;
91497 temp1R = (mid1 - side1) << shift;
91498 temp2R = (mid2 - side2) << shift;
91499 temp3R = (mid3 - side3) << shift;
91500 pOutputSamples[i*8+0] = (ma_int32)temp0L * factor;
91501 pOutputSamples[i*8+1] = (ma_int32)temp0R * factor;
91502 pOutputSamples[i*8+2] = (ma_int32)temp1L * factor;
91503 pOutputSamples[i*8+3] = (ma_int32)temp1R * factor;
91504 pOutputSamples[i*8+4] = (ma_int32)temp2L * factor;
91505 pOutputSamples[i*8+5] = (ma_int32)temp2R * factor;
91506 pOutputSamples[i*8+6] = (ma_int32)temp3L * factor;
91507 pOutputSamples[i*8+7] = (ma_int32)temp3R * factor;
91508 }
91509 } else {
91510 for (i = 0; i < frameCount4; ++i) {
91511 ma_uint32 temp0L;
91512 ma_uint32 temp1L;
91513 ma_uint32 temp2L;
91514 ma_uint32 temp3L;
91515 ma_uint32 temp0R;
91516 ma_uint32 temp1R;
91517 ma_uint32 temp2R;
91518 ma_uint32 temp3R;
91519 ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91520 ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91521 ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91522 ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91523 ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91524 ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91525 ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91526 ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91527 mid0 = (mid0 << 1) | (side0 & 0x01);
91528 mid1 = (mid1 << 1) | (side1 & 0x01);
91529 mid2 = (mid2 << 1) | (side2 & 0x01);
91530 mid3 = (mid3 << 1) | (side3 & 0x01);
91531 temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1);
91532 temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1);
91533 temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1);
91534 temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1);
91535 temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1);
91536 temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1);
91537 temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1);
91538 temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1);
91539 pOutputSamples[i*8+0] = (ma_int32)temp0L * factor;
91540 pOutputSamples[i*8+1] = (ma_int32)temp0R * factor;
91541 pOutputSamples[i*8+2] = (ma_int32)temp1L * factor;
91542 pOutputSamples[i*8+3] = (ma_int32)temp1R * factor;
91543 pOutputSamples[i*8+4] = (ma_int32)temp2L * factor;
91544 pOutputSamples[i*8+5] = (ma_int32)temp2R * factor;
91545 pOutputSamples[i*8+6] = (ma_int32)temp3L * factor;
91546 pOutputSamples[i*8+7] = (ma_int32)temp3R * factor;
91547 }
91548 }
91549 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91550 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91551 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91552 mid = (mid << 1) | (side & 0x01);
91553 pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor;
91554 pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor;
91555 }
91556}
91557#if defined(MA_DR_FLAC_SUPPORT_SSE2)
91558static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91559{
91560 ma_uint64 i;
91561 ma_uint64 frameCount4 = frameCount >> 2;
91562 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
91563 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
91564 ma_uint32 shift = unusedBitsPerSample - 8;
91565 float factor;
91566 __m128 factor128;
91567 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
91568 factor = 1.0f / 8388608.0f;
91569 factor128 = _mm_set1_ps(factor);
91570 if (shift == 0) {
91571 for (i = 0; i < frameCount4; ++i) {
91572 __m128i mid;
91573 __m128i side;
91574 __m128i tempL;
91575 __m128i tempR;
91576 __m128 leftf;
91577 __m128 rightf;
91578 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
91579 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
91580 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
91581 tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
91582 tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
91583 leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
91584 rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
91585 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
91586 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
91587 }
91588 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91589 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91590 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91591 mid = (mid << 1) | (side & 0x01);
91592 pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor;
91593 pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor;
91594 }
91595 } else {
91596 shift -= 1;
91597 for (i = 0; i < frameCount4; ++i) {
91598 __m128i mid;
91599 __m128i side;
91600 __m128i tempL;
91601 __m128i tempR;
91602 __m128 leftf;
91603 __m128 rightf;
91604 mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
91605 side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
91606 mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
91607 tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
91608 tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
91609 leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
91610 rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
91611 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
91612 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
91613 }
91614 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91615 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91616 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91617 mid = (mid << 1) | (side & 0x01);
91618 pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor;
91619 pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor;
91620 }
91621 }
91622}
91623#endif
91624#if defined(MA_DR_FLAC_SUPPORT_NEON)
91625static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91626{
91627 ma_uint64 i;
91628 ma_uint64 frameCount4 = frameCount >> 2;
91629 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
91630 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
91631 ma_uint32 shift = unusedBitsPerSample - 8;
91632 float factor;
91633 float32x4_t factor4;
91634 int32x4_t shift4;
91635 int32x4_t wbps0_4;
91636 int32x4_t wbps1_4;
91637 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
91638 factor = 1.0f / 8388608.0f;
91639 factor4 = vdupq_n_f32(factor);
91640 wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
91641 wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
91642 if (shift == 0) {
91643 for (i = 0; i < frameCount4; ++i) {
91644 int32x4_t lefti;
91645 int32x4_t righti;
91646 float32x4_t leftf;
91647 float32x4_t rightf;
91648 uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4);
91649 uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4);
91650 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
91651 lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
91652 righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
91653 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
91654 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
91655 ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
91656 }
91657 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91658 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91659 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91660 mid = (mid << 1) | (side & 0x01);
91661 pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor;
91662 pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor;
91663 }
91664 } else {
91665 shift -= 1;
91666 shift4 = vdupq_n_s32(shift);
91667 for (i = 0; i < frameCount4; ++i) {
91668 uint32x4_t mid;
91669 uint32x4_t side;
91670 int32x4_t lefti;
91671 int32x4_t righti;
91672 float32x4_t leftf;
91673 float32x4_t rightf;
91674 mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4);
91675 side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4);
91676 mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
91677 lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
91678 righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
91679 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
91680 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
91681 ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
91682 }
91683 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91684 ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91685 ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91686 mid = (mid << 1) | (side & 0x01);
91687 pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor;
91688 pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor;
91689 }
91690 }
91691}
91692#endif
91693static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91694{
91695#if defined(MA_DR_FLAC_SUPPORT_SSE2)
91696 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
91697 ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91698 } else
91699#elif defined(MA_DR_FLAC_SUPPORT_NEON)
91700 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
91701 ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91702 } else
91703#endif
91704 {
91705#if 0
91706 ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91707#else
91708 ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91709#endif
91710 }
91711}
91712#if 0
91713static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91714{
91715 for (ma_uint64 i = 0; i < frameCount; ++i) {
91716 pOutputSamples[i*2+0] = (float)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0);
91717 pOutputSamples[i*2+1] = (float)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0);
91718 }
91719}
91720#endif
91721static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91722{
91723 ma_uint64 i;
91724 ma_uint64 frameCount4 = frameCount >> 2;
91725 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
91726 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
91727 ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
91728 ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
91729 float factor = 1 / 2147483648.0;
91730 for (i = 0; i < frameCount4; ++i) {
91731 ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
91732 ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
91733 ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
91734 ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
91735 ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
91736 ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
91737 ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
91738 ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
91739 pOutputSamples[i*8+0] = (ma_int32)tempL0 * factor;
91740 pOutputSamples[i*8+1] = (ma_int32)tempR0 * factor;
91741 pOutputSamples[i*8+2] = (ma_int32)tempL1 * factor;
91742 pOutputSamples[i*8+3] = (ma_int32)tempR1 * factor;
91743 pOutputSamples[i*8+4] = (ma_int32)tempL2 * factor;
91744 pOutputSamples[i*8+5] = (ma_int32)tempR2 * factor;
91745 pOutputSamples[i*8+6] = (ma_int32)tempL3 * factor;
91746 pOutputSamples[i*8+7] = (ma_int32)tempR3 * factor;
91747 }
91748 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91749 pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor;
91750 pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor;
91751 }
91752}
91753#if defined(MA_DR_FLAC_SUPPORT_SSE2)
91754static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91755{
91756 ma_uint64 i;
91757 ma_uint64 frameCount4 = frameCount >> 2;
91758 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
91759 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
91760 ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
91761 ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
91762 float factor = 1.0f / 8388608.0f;
91763 __m128 factor128 = _mm_set1_ps(factor);
91764 for (i = 0; i < frameCount4; ++i) {
91765 __m128i lefti;
91766 __m128i righti;
91767 __m128 leftf;
91768 __m128 rightf;
91769 lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
91770 righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
91771 leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128);
91772 rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128);
91773 _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
91774 _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
91775 }
91776 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91777 pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor;
91778 pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor;
91779 }
91780}
91781#endif
91782#if defined(MA_DR_FLAC_SUPPORT_NEON)
91783static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91784{
91785 ma_uint64 i;
91786 ma_uint64 frameCount4 = frameCount >> 2;
91787 const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
91788 const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
91789 ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
91790 ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
91791 float factor = 1.0f / 8388608.0f;
91792 float32x4_t factor4 = vdupq_n_f32(factor);
91793 int32x4_t shift0_4 = vdupq_n_s32(shift0);
91794 int32x4_t shift1_4 = vdupq_n_s32(shift1);
91795 for (i = 0; i < frameCount4; ++i) {
91796 int32x4_t lefti;
91797 int32x4_t righti;
91798 float32x4_t leftf;
91799 float32x4_t rightf;
91800 lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));
91801 righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));
91802 leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
91803 rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
91804 ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
91805 }
91806 for (i = (frameCount4 << 2); i < frameCount; ++i) {
91807 pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor;
91808 pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor;
91809 }
91810}
91811#endif
91812static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
91813{
91814#if defined(MA_DR_FLAC_SUPPORT_SSE2)
91815 if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
91816 ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91817 } else
91818#elif defined(MA_DR_FLAC_SUPPORT_NEON)
91819 if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
91820 ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91821 } else
91822#endif
91823 {
91824#if 0
91825 ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91826#else
91827 ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
91828#endif
91829 }
91830}
91831MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut)
91832{
91833 ma_uint64 framesRead;
91834 ma_uint32 unusedBitsPerSample;
91835 if (pFlac == NULL || framesToRead == 0) {
91836 return 0;
91837 }
91838 if (pBufferOut == NULL) {
91839 return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead);
91840 }
91841 MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32);
91842 unusedBitsPerSample = 32 - pFlac->bitsPerSample;
91843 framesRead = 0;
91844 while (framesToRead > 0) {
91845 if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
91846 if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {
91847 break;
91848 }
91849 } else {
91850 unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
91851 ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
91852 ma_uint64 frameCountThisIteration = framesToRead;
91853 if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
91854 frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
91855 }
91856 if (channelCount == 2) {
91857 const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
91858 const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
91859 switch (pFlac->currentFLACFrame.header.channelAssignment)
91860 {
91861 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
91862 {
91863 ma_dr_flac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
91864 } break;
91865 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
91866 {
91867 ma_dr_flac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
91868 } break;
91869 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
91870 {
91871 ma_dr_flac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
91872 } break;
91873 case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
91874 default:
91875 {
91876 ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
91877 } break;
91878 }
91879 } else {
91880 ma_uint64 i;
91881 for (i = 0; i < frameCountThisIteration; ++i) {
91882 unsigned int j;
91883 for (j = 0; j < channelCount; ++j) {
91884 ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
91885 pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0);
91886 }
91887 }
91888 }
91889 framesRead += frameCountThisIteration;
91890 pBufferOut += frameCountThisIteration * channelCount;
91891 framesToRead -= frameCountThisIteration;
91892 pFlac->currentPCMFrame += frameCountThisIteration;
91893 pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration;
91894 }
91895 }
91896 return framesRead;
91897}
91898MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)
91899{
91900 if (pFlac == NULL) {
91901 return MA_FALSE;
91902 }
91903 if (pFlac->currentPCMFrame == pcmFrameIndex) {
91904 return MA_TRUE;
91905 }
91906 if (pFlac->firstFLACFramePosInBytes == 0) {
91907 return MA_FALSE;
91908 }
91909 if (pcmFrameIndex == 0) {
91910 pFlac->currentPCMFrame = 0;
91911 return ma_dr_flac__seek_to_first_frame(pFlac);
91912 } else {
91913 ma_bool32 wasSuccessful = MA_FALSE;
91914 ma_uint64 originalPCMFrame = pFlac->currentPCMFrame;
91915 if (pcmFrameIndex > pFlac->totalPCMFrameCount) {
91916 pcmFrameIndex = pFlac->totalPCMFrameCount;
91917 }
91918 if (pcmFrameIndex > pFlac->currentPCMFrame) {
91919 ma_uint32 offset = (ma_uint32)(pcmFrameIndex - pFlac->currentPCMFrame);
91920 if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) {
91921 pFlac->currentFLACFrame.pcmFramesRemaining -= offset;
91922 pFlac->currentPCMFrame = pcmFrameIndex;
91923 return MA_TRUE;
91924 }
91925 } else {
91926 ma_uint32 offsetAbs = (ma_uint32)(pFlac->currentPCMFrame - pcmFrameIndex);
91927 ma_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
91928 ma_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining;
91929 if (currentFLACFramePCMFramesConsumed > offsetAbs) {
91930 pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs;
91931 pFlac->currentPCMFrame = pcmFrameIndex;
91932 return MA_TRUE;
91933 }
91934 }
91935#ifndef MA_DR_FLAC_NO_OGG
91936 if (pFlac->container == ma_dr_flac_container_ogg)
91937 {
91938 wasSuccessful = ma_dr_flac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex);
91939 }
91940 else
91941#endif
91942 {
91943 if (!pFlac->_noSeekTableSeek) {
91944 wasSuccessful = ma_dr_flac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex);
91945 }
91946#if !defined(MA_DR_FLAC_NO_CRC)
91947 if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) {
91948 wasSuccessful = ma_dr_flac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex);
91949 }
91950#endif
91951 if (!wasSuccessful && !pFlac->_noBruteForceSeek) {
91952 wasSuccessful = ma_dr_flac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex);
91953 }
91954 }
91955 if (wasSuccessful) {
91956 pFlac->currentPCMFrame = pcmFrameIndex;
91957 } else {
91958 if (ma_dr_flac_seek_to_pcm_frame(pFlac, originalPCMFrame) == MA_FALSE) {
91959 ma_dr_flac_seek_to_pcm_frame(pFlac, 0);
91960 }
91961 }
91962 return wasSuccessful;
91963 }
91964}
91965#define MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \
91966static type* ma_dr_flac__full_read_and_close_ ## extension (ma_dr_flac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut)\
91967{ \
91968 type* pSampleData = NULL; \
91969 ma_uint64 totalPCMFrameCount; \
91970 \
91971 MA_DR_FLAC_ASSERT(pFlac != NULL); \
91972 \
91973 totalPCMFrameCount = pFlac->totalPCMFrameCount; \
91974 \
91975 if (totalPCMFrameCount == 0) { \
91976 type buffer[4096]; \
91977 ma_uint64 pcmFramesRead; \
91978 size_t sampleDataBufferSize = sizeof(buffer); \
91979 \
91980 pSampleData = (type*)ma_dr_flac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \
91981 if (pSampleData == NULL) { \
91982 goto on_error; \
91983 } \
91984 \
91985 while ((pcmFramesRead = (ma_uint64)ma_dr_flac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \
91986 if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \
91987 type* pNewSampleData; \
91988 size_t newSampleDataBufferSize; \
91989 \
91990 newSampleDataBufferSize = sampleDataBufferSize * 2; \
91991 pNewSampleData = (type*)ma_dr_flac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \
91992 if (pNewSampleData == NULL) { \
91993 ma_dr_flac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \
91994 goto on_error; \
91995 } \
91996 \
91997 sampleDataBufferSize = newSampleDataBufferSize; \
91998 pSampleData = pNewSampleData; \
91999 } \
92000 \
92001 MA_DR_FLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \
92002 totalPCMFrameCount += pcmFramesRead; \
92003 } \
92004 \
92005 \
92006 MA_DR_FLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \
92007 } else { \
92008 ma_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \
92009 if (dataSize > (ma_uint64)MA_SIZE_MAX) { \
92010 goto on_error; \
92011 } \
92012 \
92013 pSampleData = (type*)ma_dr_flac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \
92014 if (pSampleData == NULL) { \
92015 goto on_error; \
92016 } \
92017 \
92018 totalPCMFrameCount = ma_dr_flac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \
92019 } \
92020 \
92021 if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \
92022 if (channelsOut) *channelsOut = pFlac->channels; \
92023 if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \
92024 \
92025 ma_dr_flac_close(pFlac); \
92026 return pSampleData; \
92027 \
92028on_error: \
92029 ma_dr_flac_close(pFlac); \
92030 return NULL; \
92031}
92032MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s32, ma_int32)
92033MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s16, ma_int16)
92034MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float)
92035MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
92036{
92037 ma_dr_flac* pFlac;
92038 if (channelsOut) {
92039 *channelsOut = 0;
92040 }
92041 if (sampleRateOut) {
92042 *sampleRateOut = 0;
92043 }
92044 if (totalPCMFrameCountOut) {
92045 *totalPCMFrameCountOut = 0;
92046 }
92047 pFlac = ma_dr_flac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks);
92048 if (pFlac == NULL) {
92049 return NULL;
92050 }
92051 return ma_dr_flac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
92052}
92053MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
92054{
92055 ma_dr_flac* pFlac;
92056 if (channelsOut) {
92057 *channelsOut = 0;
92058 }
92059 if (sampleRateOut) {
92060 *sampleRateOut = 0;
92061 }
92062 if (totalPCMFrameCountOut) {
92063 *totalPCMFrameCountOut = 0;
92064 }
92065 pFlac = ma_dr_flac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks);
92066 if (pFlac == NULL) {
92067 return NULL;
92068 }
92069 return ma_dr_flac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
92070}
92071MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
92072{
92073 ma_dr_flac* pFlac;
92074 if (channelsOut) {
92075 *channelsOut = 0;
92076 }
92077 if (sampleRateOut) {
92078 *sampleRateOut = 0;
92079 }
92080 if (totalPCMFrameCountOut) {
92081 *totalPCMFrameCountOut = 0;
92082 }
92083 pFlac = ma_dr_flac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks);
92084 if (pFlac == NULL) {
92085 return NULL;
92086 }
92087 return ma_dr_flac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
92088}
92089#ifndef MA_DR_FLAC_NO_STDIO
92090MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
92091{
92092 ma_dr_flac* pFlac;
92093 if (sampleRate) {
92094 *sampleRate = 0;
92095 }
92096 if (channels) {
92097 *channels = 0;
92098 }
92099 if (totalPCMFrameCount) {
92100 *totalPCMFrameCount = 0;
92101 }
92102 pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks);
92103 if (pFlac == NULL) {
92104 return NULL;
92105 }
92106 return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
92107}
92108MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
92109{
92110 ma_dr_flac* pFlac;
92111 if (sampleRate) {
92112 *sampleRate = 0;
92113 }
92114 if (channels) {
92115 *channels = 0;
92116 }
92117 if (totalPCMFrameCount) {
92118 *totalPCMFrameCount = 0;
92119 }
92120 pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks);
92121 if (pFlac == NULL) {
92122 return NULL;
92123 }
92124 return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
92125}
92126MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
92127{
92128 ma_dr_flac* pFlac;
92129 if (sampleRate) {
92130 *sampleRate = 0;
92131 }
92132 if (channels) {
92133 *channels = 0;
92134 }
92135 if (totalPCMFrameCount) {
92136 *totalPCMFrameCount = 0;
92137 }
92138 pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks);
92139 if (pFlac == NULL) {
92140 return NULL;
92141 }
92142 return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
92143}
92144#endif
92145MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
92146{
92147 ma_dr_flac* pFlac;
92148 if (sampleRate) {
92149 *sampleRate = 0;
92150 }
92151 if (channels) {
92152 *channels = 0;
92153 }
92154 if (totalPCMFrameCount) {
92155 *totalPCMFrameCount = 0;
92156 }
92157 pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks);
92158 if (pFlac == NULL) {
92159 return NULL;
92160 }
92161 return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
92162}
92163MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
92164{
92165 ma_dr_flac* pFlac;
92166 if (sampleRate) {
92167 *sampleRate = 0;
92168 }
92169 if (channels) {
92170 *channels = 0;
92171 }
92172 if (totalPCMFrameCount) {
92173 *totalPCMFrameCount = 0;
92174 }
92175 pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks);
92176 if (pFlac == NULL) {
92177 return NULL;
92178 }
92179 return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
92180}
92181MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
92182{
92183 ma_dr_flac* pFlac;
92184 if (sampleRate) {
92185 *sampleRate = 0;
92186 }
92187 if (channels) {
92188 *channels = 0;
92189 }
92190 if (totalPCMFrameCount) {
92191 *totalPCMFrameCount = 0;
92192 }
92193 pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks);
92194 if (pFlac == NULL) {
92195 return NULL;
92196 }
92197 return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
92198}
92199MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
92200{
92201 if (pAllocationCallbacks != NULL) {
92202 ma_dr_flac__free_from_callbacks(p, pAllocationCallbacks);
92203 } else {
92204 ma_dr_flac__free_default(p, NULL);
92205 }
92206}
92207MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments)
92208{
92209 if (pIter == NULL) {
92210 return;
92211 }
92212 pIter->countRemaining = commentCount;
92213 pIter->pRunningData = (const char*)pComments;
92214}
92215MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut)
92216{
92217 ma_int32 length;
92218 const char* pComment;
92219 if (pCommentLengthOut) {
92220 *pCommentLengthOut = 0;
92221 }
92222 if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
92223 return NULL;
92224 }
92225 length = ma_dr_flac__le2host_32_ptr_unaligned(pIter->pRunningData);
92226 pIter->pRunningData += 4;
92227 pComment = pIter->pRunningData;
92228 pIter->pRunningData += length;
92229 pIter->countRemaining -= 1;
92230 if (pCommentLengthOut) {
92231 *pCommentLengthOut = length;
92232 }
92233 return pComment;
92234}
92235MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData)
92236{
92237 if (pIter == NULL) {
92238 return;
92239 }
92240 pIter->countRemaining = trackCount;
92241 pIter->pRunningData = (const char*)pTrackData;
92242}
92243MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack)
92244{
92245 ma_dr_flac_cuesheet_track cuesheetTrack;
92246 const char* pRunningData;
92247 ma_uint64 offsetHi;
92248 ma_uint64 offsetLo;
92249 if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
92250 return MA_FALSE;
92251 }
92252 pRunningData = pIter->pRunningData;
92253 offsetHi = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4;
92254 offsetLo = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4;
92255 cuesheetTrack.offset = offsetLo | (offsetHi << 32);
92256 cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1;
92257 MA_DR_FLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12;
92258 cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0;
92259 cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14;
92260 cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1;
92261 cuesheetTrack.pIndexPoints = (const ma_dr_flac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(ma_dr_flac_cuesheet_track_index);
92262 pIter->pRunningData = pRunningData;
92263 pIter->countRemaining -= 1;
92264 if (pCuesheetTrack) {
92265 *pCuesheetTrack = cuesheetTrack;
92266 }
92267 return MA_TRUE;
92268}
92269#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
92270 #pragma GCC diagnostic pop
92271#endif
92272#endif
92273/* dr_flac_c end */
92274#endif /* MA_DR_FLAC_IMPLEMENTATION */
92275#endif /* MA_NO_FLAC */
92276
92277#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
92278#if !defined(MA_DR_MP3_IMPLEMENTATION)
92279/* dr_mp3_c begin */
92280#ifndef ma_dr_mp3_c
92281#define ma_dr_mp3_c
92282#include <stdlib.h>
92283#include <string.h>
92284#include <limits.h>
92285MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)
92286{
92287 if (pMajor) {
92288 *pMajor = MA_DR_MP3_VERSION_MAJOR;
92289 }
92290 if (pMinor) {
92291 *pMinor = MA_DR_MP3_VERSION_MINOR;
92292 }
92293 if (pRevision) {
92294 *pRevision = MA_DR_MP3_VERSION_REVISION;
92295 }
92296}
92297MA_API const char* ma_dr_mp3_version_string(void)
92298{
92299 return MA_DR_MP3_VERSION_STRING;
92300}
92301#if defined(__TINYC__)
92302#define MA_DR_MP3_NO_SIMD
92303#endif
92304#define MA_DR_MP3_OFFSET_PTR(p, offset) ((void*)((ma_uint8*)(p) + (offset)))
92305#define MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE 2304
92306#ifndef MA_DR_MP3_MAX_FRAME_SYNC_MATCHES
92307#define MA_DR_MP3_MAX_FRAME_SYNC_MATCHES 10
92308#endif
92309#define MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE
92310#define MA_DR_MP3_MAX_BITRESERVOIR_BYTES 511
92311#define MA_DR_MP3_SHORT_BLOCK_TYPE 2
92312#define MA_DR_MP3_STOP_BLOCK_TYPE 3
92313#define MA_DR_MP3_MODE_MONO 3
92314#define MA_DR_MP3_MODE_JOINT_STEREO 1
92315#define MA_DR_MP3_HDR_SIZE 4
92316#define MA_DR_MP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0)
92317#define MA_DR_MP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60)
92318#define MA_DR_MP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0)
92319#define MA_DR_MP3_HDR_IS_CRC(h) (!((h[1]) & 1))
92320#define MA_DR_MP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2)
92321#define MA_DR_MP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8)
92322#define MA_DR_MP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10)
92323#define MA_DR_MP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10)
92324#define MA_DR_MP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20)
92325#define MA_DR_MP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3)
92326#define MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3)
92327#define MA_DR_MP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3)
92328#define MA_DR_MP3_HDR_GET_BITRATE(h) ((h[2]) >> 4)
92329#define MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3)
92330#define MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h) (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3)
92331#define MA_DR_MP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2)
92332#define MA_DR_MP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6)
92333#define MA_DR_MP3_BITS_DEQUANTIZER_OUT -1
92334#define MA_DR_MP3_MAX_SCF (255 + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210)
92335#define MA_DR_MP3_MAX_SCFI ((MA_DR_MP3_MAX_SCF + 3) & ~3)
92336#define MA_DR_MP3_MIN(a, b) ((a) > (b) ? (b) : (a))
92337#define MA_DR_MP3_MAX(a, b) ((a) < (b) ? (b) : (a))
92338#if !defined(MA_DR_MP3_NO_SIMD)
92339#if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC))
92340#define MA_DR_MP3_ONLY_SIMD
92341#endif
92342#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__)))
92343#if defined(_MSC_VER)
92344#include <intrin.h>
92345#endif
92346#include <emmintrin.h>
92347#define MA_DR_MP3_HAVE_SSE 1
92348#define MA_DR_MP3_HAVE_SIMD 1
92349#define MA_DR_MP3_VSTORE _mm_storeu_ps
92350#define MA_DR_MP3_VLD _mm_loadu_ps
92351#define MA_DR_MP3_VSET _mm_set1_ps
92352#define MA_DR_MP3_VADD _mm_add_ps
92353#define MA_DR_MP3_VSUB _mm_sub_ps
92354#define MA_DR_MP3_VMUL _mm_mul_ps
92355#define MA_DR_MP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y))
92356#define MA_DR_MP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y))
92357#define MA_DR_MP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s))
92358#define MA_DR_MP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3))
92359typedef __m128 ma_dr_mp3_f4;
92360#if defined(_MSC_VER) || defined(MA_DR_MP3_ONLY_SIMD)
92361#define ma_dr_mp3_cpuid __cpuid
92362#else
92363static __inline__ __attribute__((always_inline)) void ma_dr_mp3_cpuid(int CPUInfo[], const int InfoType)
92364{
92365#if defined(__PIC__)
92366 __asm__ __volatile__(
92367#if defined(__x86_64__)
92368 "push %%rbx\n"
92369 "cpuid\n"
92370 "xchgl %%ebx, %1\n"
92371 "pop %%rbx\n"
92372#else
92373 "xchgl %%ebx, %1\n"
92374 "cpuid\n"
92375 "xchgl %%ebx, %1\n"
92376#endif
92377 : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3])
92378 : "a" (InfoType));
92379#else
92380 __asm__ __volatile__(
92381 "cpuid"
92382 : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3])
92383 : "a" (InfoType));
92384#endif
92385}
92386#endif
92387static int ma_dr_mp3_have_simd(void)
92388{
92389#ifdef MA_DR_MP3_ONLY_SIMD
92390 return 1;
92391#else
92392 static int g_have_simd;
92393 int CPUInfo[4];
92394#ifdef MINIMP3_TEST
92395 static int g_counter;
92396 if (g_counter++ > 100)
92397 return 0;
92398#endif
92399 if (g_have_simd)
92400 goto end;
92401 ma_dr_mp3_cpuid(CPUInfo, 0);
92402 if (CPUInfo[0] > 0)
92403 {
92404 ma_dr_mp3_cpuid(CPUInfo, 1);
92405 g_have_simd = (CPUInfo[3] & (1 << 26)) + 1;
92406 return g_have_simd - 1;
92407 }
92408end:
92409 return g_have_simd - 1;
92410#endif
92411}
92412#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
92413#include <arm_neon.h>
92414#define MA_DR_MP3_HAVE_SSE 0
92415#define MA_DR_MP3_HAVE_SIMD 1
92416#define MA_DR_MP3_VSTORE vst1q_f32
92417#define MA_DR_MP3_VLD vld1q_f32
92418#define MA_DR_MP3_VSET vmovq_n_f32
92419#define MA_DR_MP3_VADD vaddq_f32
92420#define MA_DR_MP3_VSUB vsubq_f32
92421#define MA_DR_MP3_VMUL vmulq_f32
92422#define MA_DR_MP3_VMAC(a, x, y) vmlaq_f32(a, x, y)
92423#define MA_DR_MP3_VMSB(a, x, y) vmlsq_f32(a, x, y)
92424#define MA_DR_MP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s))
92425#define MA_DR_MP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x)))
92426typedef float32x4_t ma_dr_mp3_f4;
92427static int ma_dr_mp3_have_simd(void)
92428{
92429 return 1;
92430}
92431#else
92432#define MA_DR_MP3_HAVE_SSE 0
92433#define MA_DR_MP3_HAVE_SIMD 0
92434#ifdef MA_DR_MP3_ONLY_SIMD
92435#error MA_DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled
92436#endif
92437#endif
92438#else
92439#define MA_DR_MP3_HAVE_SIMD 0
92440#endif
92441#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(_M_ARM64EC) && !defined(__ARM_ARCH_6M__)
92442#define MA_DR_MP3_HAVE_ARMV6 1
92443static __inline__ __attribute__((always_inline)) ma_int32 ma_dr_mp3_clip_int16_arm(ma_int32 a)
92444{
92445 ma_int32 x = 0;
92446 __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a));
92447 return x;
92448}
92449#else
92450#define MA_DR_MP3_HAVE_ARMV6 0
92451#endif
92452#ifndef MA_DR_MP3_ASSERT
92453#include <assert.h>
92454#define MA_DR_MP3_ASSERT(expression) assert(expression)
92455#endif
92456#ifndef MA_DR_MP3_COPY_MEMORY
92457#define MA_DR_MP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
92458#endif
92459#ifndef MA_DR_MP3_MOVE_MEMORY
92460#define MA_DR_MP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz))
92461#endif
92462#ifndef MA_DR_MP3_ZERO_MEMORY
92463#define MA_DR_MP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
92464#endif
92465#define MA_DR_MP3_ZERO_OBJECT(p) MA_DR_MP3_ZERO_MEMORY((p), sizeof(*(p)))
92466#ifndef MA_DR_MP3_MALLOC
92467#define MA_DR_MP3_MALLOC(sz) malloc((sz))
92468#endif
92469#ifndef MA_DR_MP3_REALLOC
92470#define MA_DR_MP3_REALLOC(p, sz) realloc((p), (sz))
92471#endif
92472#ifndef MA_DR_MP3_FREE
92473#define MA_DR_MP3_FREE(p) free((p))
92474#endif
92475typedef struct
92476{
92477 const ma_uint8 *buf;
92478 int pos, limit;
92479} ma_dr_mp3_bs;
92480typedef struct
92481{
92482 float scf[3*64];
92483 ma_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64];
92484} ma_dr_mp3_L12_scale_info;
92485typedef struct
92486{
92487 ma_uint8 tab_offset, code_tab_width, band_count;
92488} ma_dr_mp3_L12_subband_alloc;
92489typedef struct
92490{
92491 const ma_uint8 *sfbtab;
92492 ma_uint16 part_23_length, big_values, scalefac_compress;
92493 ma_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb;
92494 ma_uint8 table_select[3], region_count[3], subblock_gain[3];
92495 ma_uint8 preflag, scalefac_scale, count1_table, scfsi;
92496} ma_dr_mp3_L3_gr_info;
92497typedef struct
92498{
92499 ma_dr_mp3_bs bs;
92500 ma_uint8 maindata[MA_DR_MP3_MAX_BITRESERVOIR_BYTES + MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES];
92501 ma_dr_mp3_L3_gr_info gr_info[4];
92502 float grbuf[2][576], scf[40], syn[18 + 15][2*32];
92503 ma_uint8 ist_pos[2][39];
92504} ma_dr_mp3dec_scratch;
92505static void ma_dr_mp3_bs_init(ma_dr_mp3_bs *bs, const ma_uint8 *data, int bytes)
92506{
92507 bs->buf = data;
92508 bs->pos = 0;
92509 bs->limit = bytes*8;
92510}
92511static ma_uint32 ma_dr_mp3_bs_get_bits(ma_dr_mp3_bs *bs, int n)
92512{
92513 ma_uint32 next, cache = 0, s = bs->pos & 7;
92514 int shl = n + s;
92515 const ma_uint8 *p = bs->buf + (bs->pos >> 3);
92516 if ((bs->pos += n) > bs->limit)
92517 return 0;
92518 next = *p++ & (255 >> s);
92519 while ((shl -= 8) > 0)
92520 {
92521 cache |= next << shl;
92522 next = *p++;
92523 }
92524 return cache | (next >> -shl);
92525}
92526static int ma_dr_mp3_hdr_valid(const ma_uint8 *h)
92527{
92528 return h[0] == 0xff &&
92529 ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) &&
92530 (MA_DR_MP3_HDR_GET_LAYER(h) != 0) &&
92531 (MA_DR_MP3_HDR_GET_BITRATE(h) != 15) &&
92532 (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) != 3);
92533}
92534static int ma_dr_mp3_hdr_compare(const ma_uint8 *h1, const ma_uint8 *h2)
92535{
92536 return ma_dr_mp3_hdr_valid(h2) &&
92537 ((h1[1] ^ h2[1]) & 0xFE) == 0 &&
92538 ((h1[2] ^ h2[2]) & 0x0C) == 0 &&
92539 !(MA_DR_MP3_HDR_IS_FREE_FORMAT(h1) ^ MA_DR_MP3_HDR_IS_FREE_FORMAT(h2));
92540}
92541static unsigned ma_dr_mp3_hdr_bitrate_kbps(const ma_uint8 *h)
92542{
92543 static const ma_uint8 halfrate[2][3][15] = {
92544 { { 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 } },
92545 { { 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 } },
92546 };
92547 return 2*halfrate[!!MA_DR_MP3_HDR_TEST_MPEG1(h)][MA_DR_MP3_HDR_GET_LAYER(h) - 1][MA_DR_MP3_HDR_GET_BITRATE(h)];
92548}
92549static unsigned ma_dr_mp3_hdr_sample_rate_hz(const ma_uint8 *h)
92550{
92551 static const unsigned g_hz[3] = { 44100, 48000, 32000 };
92552 return g_hz[MA_DR_MP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!MA_DR_MP3_HDR_TEST_MPEG1(h) >> (int)!MA_DR_MP3_HDR_TEST_NOT_MPEG25(h);
92553}
92554static unsigned ma_dr_mp3_hdr_frame_samples(const ma_uint8 *h)
92555{
92556 return MA_DR_MP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)MA_DR_MP3_HDR_IS_FRAME_576(h));
92557}
92558static int ma_dr_mp3_hdr_frame_bytes(const ma_uint8 *h, int free_format_size)
92559{
92560 int frame_bytes = ma_dr_mp3_hdr_frame_samples(h)*ma_dr_mp3_hdr_bitrate_kbps(h)*125/ma_dr_mp3_hdr_sample_rate_hz(h);
92561 if (MA_DR_MP3_HDR_IS_LAYER_1(h))
92562 {
92563 frame_bytes &= ~3;
92564 }
92565 return frame_bytes ? frame_bytes : free_format_size;
92566}
92567static int ma_dr_mp3_hdr_padding(const ma_uint8 *h)
92568{
92569 return MA_DR_MP3_HDR_TEST_PADDING(h) ? (MA_DR_MP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0;
92570}
92571#ifndef MA_DR_MP3_ONLY_MP3
92572static const ma_dr_mp3_L12_subband_alloc *ma_dr_mp3_L12_subband_alloc_table(const ma_uint8 *hdr, ma_dr_mp3_L12_scale_info *sci)
92573{
92574 const ma_dr_mp3_L12_subband_alloc *alloc;
92575 int mode = MA_DR_MP3_HDR_GET_STEREO_MODE(hdr);
92576 int nbands, stereo_bands = (mode == MA_DR_MP3_MODE_MONO) ? 0 : (mode == MA_DR_MP3_MODE_JOINT_STEREO) ? (MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32;
92577 if (MA_DR_MP3_HDR_IS_LAYER_1(hdr))
92578 {
92579 static const ma_dr_mp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } };
92580 alloc = g_alloc_L1;
92581 nbands = 32;
92582 } else if (!MA_DR_MP3_HDR_TEST_MPEG1(hdr))
92583 {
92584 static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } };
92585 alloc = g_alloc_L2M2;
92586 nbands = 30;
92587 } else
92588 {
92589 static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } };
92590 int sample_rate_idx = MA_DR_MP3_HDR_GET_SAMPLE_RATE(hdr);
92591 unsigned kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr) >> (int)(mode != MA_DR_MP3_MODE_MONO);
92592 if (!kbps)
92593 {
92594 kbps = 192;
92595 }
92596 alloc = g_alloc_L2M1;
92597 nbands = 27;
92598 if (kbps < 56)
92599 {
92600 static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } };
92601 alloc = g_alloc_L2M1_lowrate;
92602 nbands = sample_rate_idx == 2 ? 12 : 8;
92603 } else if (kbps >= 96 && sample_rate_idx != 1)
92604 {
92605 nbands = 30;
92606 }
92607 }
92608 sci->total_bands = (ma_uint8)nbands;
92609 sci->stereo_bands = (ma_uint8)MA_DR_MP3_MIN(stereo_bands, nbands);
92610 return alloc;
92611}
92612static void ma_dr_mp3_L12_read_scalefactors(ma_dr_mp3_bs *bs, ma_uint8 *pba, ma_uint8 *scfcod, int bands, float *scf)
92613{
92614 static const float g_deq_L12[18*3] = {
92615#define MA_DR_MP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x
92616 MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(7),MA_DR_MP3_DQ(15),MA_DR_MP3_DQ(31),MA_DR_MP3_DQ(63),MA_DR_MP3_DQ(127),MA_DR_MP3_DQ(255),MA_DR_MP3_DQ(511),MA_DR_MP3_DQ(1023),MA_DR_MP3_DQ(2047),MA_DR_MP3_DQ(4095),MA_DR_MP3_DQ(8191),MA_DR_MP3_DQ(16383),MA_DR_MP3_DQ(32767),MA_DR_MP3_DQ(65535),MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(5),MA_DR_MP3_DQ(9)
92617 };
92618 int i, m;
92619 for (i = 0; i < bands; i++)
92620 {
92621 float s = 0;
92622 int ba = *pba++;
92623 int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0;
92624 for (m = 4; m; m >>= 1)
92625 {
92626 if (mask & m)
92627 {
92628 int b = ma_dr_mp3_bs_get_bits(bs, 6);
92629 s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3);
92630 }
92631 *scf++ = s;
92632 }
92633 }
92634}
92635static void ma_dr_mp3_L12_read_scale_info(const ma_uint8 *hdr, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci)
92636{
92637 static const ma_uint8 g_bitalloc_code_tab[] = {
92638 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16,
92639 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16,
92640 0,17,18, 3,19,4,5,16,
92641 0,17,18,16,
92642 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15,
92643 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14,
92644 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16
92645 };
92646 const ma_dr_mp3_L12_subband_alloc *subband_alloc = ma_dr_mp3_L12_subband_alloc_table(hdr, sci);
92647 int i, k = 0, ba_bits = 0;
92648 const ma_uint8 *ba_code_tab = g_bitalloc_code_tab;
92649 for (i = 0; i < sci->total_bands; i++)
92650 {
92651 ma_uint8 ba;
92652 if (i == k)
92653 {
92654 k += subband_alloc->band_count;
92655 ba_bits = subband_alloc->code_tab_width;
92656 ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset;
92657 subband_alloc++;
92658 }
92659 ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)];
92660 sci->bitalloc[2*i] = ba;
92661 if (i < sci->stereo_bands)
92662 {
92663 ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)];
92664 }
92665 sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0;
92666 }
92667 for (i = 0; i < 2*sci->total_bands; i++)
92668 {
92669 sci->scfcod[i] = (ma_uint8)(sci->bitalloc[i] ? MA_DR_MP3_HDR_IS_LAYER_1(hdr) ? 2 : ma_dr_mp3_bs_get_bits(bs, 2) : 6);
92670 }
92671 ma_dr_mp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf);
92672 for (i = sci->stereo_bands; i < sci->total_bands; i++)
92673 {
92674 sci->bitalloc[2*i + 1] = 0;
92675 }
92676}
92677static int ma_dr_mp3_L12_dequantize_granule(float *grbuf, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci, int group_size)
92678{
92679 int i, j, k, choff = 576;
92680 for (j = 0; j < 4; j++)
92681 {
92682 float *dst = grbuf + group_size*j;
92683 for (i = 0; i < 2*sci->total_bands; i++)
92684 {
92685 int ba = sci->bitalloc[i];
92686 if (ba != 0)
92687 {
92688 if (ba < 17)
92689 {
92690 int half = (1 << (ba - 1)) - 1;
92691 for (k = 0; k < group_size; k++)
92692 {
92693 dst[k] = (float)((int)ma_dr_mp3_bs_get_bits(bs, ba) - half);
92694 }
92695 } else
92696 {
92697 unsigned mod = (2 << (ba - 17)) + 1;
92698 unsigned code = ma_dr_mp3_bs_get_bits(bs, mod + 2 - (mod >> 3));
92699 for (k = 0; k < group_size; k++, code /= mod)
92700 {
92701 dst[k] = (float)((int)(code % mod - mod/2));
92702 }
92703 }
92704 }
92705 dst += choff;
92706 choff = 18 - choff;
92707 }
92708 }
92709 return group_size*4;
92710}
92711static void ma_dr_mp3_L12_apply_scf_384(ma_dr_mp3_L12_scale_info *sci, const float *scf, float *dst)
92712{
92713 int i, k;
92714 MA_DR_MP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float));
92715 for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6)
92716 {
92717 for (k = 0; k < 12; k++)
92718 {
92719 dst[k + 0] *= scf[0];
92720 dst[k + 576] *= scf[3];
92721 }
92722 }
92723}
92724#endif
92725static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr)
92726{
92727 static const ma_uint8 g_scf_long[8][23] = {
92728 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
92729 { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 },
92730 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
92731 { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 },
92732 { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
92733 { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 },
92734 { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 },
92735 { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 }
92736 };
92737 static const ma_uint8 g_scf_short[8][40] = {
92738 { 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 },
92739 { 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 },
92740 { 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 },
92741 { 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 },
92742 { 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 },
92743 { 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 },
92744 { 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 },
92745 { 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 }
92746 };
92747 static const ma_uint8 g_scf_mixed[8][40] = {
92748 { 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 },
92749 { 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 },
92750 { 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 },
92751 { 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 },
92752 { 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 },
92753 { 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 },
92754 { 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 },
92755 { 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 }
92756 };
92757 unsigned tables, scfsi = 0;
92758 int main_data_begin, part_23_sum = 0;
92759 int gr_count = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2;
92760 int sr_idx = MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0);
92761 if (MA_DR_MP3_HDR_TEST_MPEG1(hdr))
92762 {
92763 gr_count *= 2;
92764 main_data_begin = ma_dr_mp3_bs_get_bits(bs, 9);
92765 scfsi = ma_dr_mp3_bs_get_bits(bs, 7 + gr_count);
92766 } else
92767 {
92768 main_data_begin = ma_dr_mp3_bs_get_bits(bs, 8 + gr_count) >> gr_count;
92769 }
92770 do
92771 {
92772 if (MA_DR_MP3_HDR_IS_MONO(hdr))
92773 {
92774 scfsi <<= 4;
92775 }
92776 gr->part_23_length = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 12);
92777 part_23_sum += gr->part_23_length;
92778 gr->big_values = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 9);
92779 if (gr->big_values > 288)
92780 {
92781 return -1;
92782 }
92783 gr->global_gain = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 8);
92784 gr->scalefac_compress = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 4 : 9);
92785 gr->sfbtab = g_scf_long[sr_idx];
92786 gr->n_long_sfb = 22;
92787 gr->n_short_sfb = 0;
92788 if (ma_dr_mp3_bs_get_bits(bs, 1))
92789 {
92790 gr->block_type = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 2);
92791 if (!gr->block_type)
92792 {
92793 return -1;
92794 }
92795 gr->mixed_block_flag = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1);
92796 gr->region_count[0] = 7;
92797 gr->region_count[1] = 255;
92798 if (gr->block_type == MA_DR_MP3_SHORT_BLOCK_TYPE)
92799 {
92800 scfsi &= 0x0F0F;
92801 if (!gr->mixed_block_flag)
92802 {
92803 gr->region_count[0] = 8;
92804 gr->sfbtab = g_scf_short[sr_idx];
92805 gr->n_long_sfb = 0;
92806 gr->n_short_sfb = 39;
92807 } else
92808 {
92809 gr->sfbtab = g_scf_mixed[sr_idx];
92810 gr->n_long_sfb = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 8 : 6;
92811 gr->n_short_sfb = 30;
92812 }
92813 }
92814 tables = ma_dr_mp3_bs_get_bits(bs, 10);
92815 tables <<= 5;
92816 gr->subblock_gain[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3);
92817 gr->subblock_gain[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3);
92818 gr->subblock_gain[2] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3);
92819 } else
92820 {
92821 gr->block_type = 0;
92822 gr->mixed_block_flag = 0;
92823 tables = ma_dr_mp3_bs_get_bits(bs, 15);
92824 gr->region_count[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 4);
92825 gr->region_count[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3);
92826 gr->region_count[2] = 255;
92827 }
92828 gr->table_select[0] = (ma_uint8)(tables >> 10);
92829 gr->table_select[1] = (ma_uint8)((tables >> 5) & 31);
92830 gr->table_select[2] = (ma_uint8)((tables) & 31);
92831 gr->preflag = (ma_uint8)(MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? ma_dr_mp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500));
92832 gr->scalefac_scale = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1);
92833 gr->count1_table = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1);
92834 gr->scfsi = (ma_uint8)((scfsi >> 12) & 15);
92835 scfsi <<= 4;
92836 gr++;
92837 } while(--gr_count);
92838 if (part_23_sum + bs->pos > bs->limit + main_data_begin*8)
92839 {
92840 return -1;
92841 }
92842 return main_data_begin;
92843}
92844static void ma_dr_mp3_L3_read_scalefactors(ma_uint8 *scf, ma_uint8 *ist_pos, const ma_uint8 *scf_size, const ma_uint8 *scf_count, ma_dr_mp3_bs *bitbuf, int scfsi)
92845{
92846 int i, k;
92847 for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2)
92848 {
92849 int cnt = scf_count[i];
92850 if (scfsi & 8)
92851 {
92852 MA_DR_MP3_COPY_MEMORY(scf, ist_pos, cnt);
92853 } else
92854 {
92855 int bits = scf_size[i];
92856 if (!bits)
92857 {
92858 MA_DR_MP3_ZERO_MEMORY(scf, cnt);
92859 MA_DR_MP3_ZERO_MEMORY(ist_pos, cnt);
92860 } else
92861 {
92862 int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1;
92863 for (k = 0; k < cnt; k++)
92864 {
92865 int s = ma_dr_mp3_bs_get_bits(bitbuf, bits);
92866 ist_pos[k] = (ma_uint8)(s == max_scf ? -1 : s);
92867 scf[k] = (ma_uint8)s;
92868 }
92869 }
92870 }
92871 ist_pos += cnt;
92872 scf += cnt;
92873 }
92874 scf[0] = scf[1] = scf[2] = 0;
92875}
92876static float ma_dr_mp3_L3_ldexp_q2(float y, int exp_q2)
92877{
92878 static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f };
92879 int e;
92880 do
92881 {
92882 e = MA_DR_MP3_MIN(30*4, exp_q2);
92883 y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2));
92884 } while ((exp_q2 -= e) > 0);
92885 return y;
92886}
92887static void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_pos, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr, float *scf, int ch)
92888{
92889 static const ma_uint8 g_scf_partitions[3][28] = {
92890 { 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 },
92891 { 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 },
92892 { 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 }
92893 };
92894 const ma_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb];
92895 ma_uint8 scf_size[4], iscf[40];
92896 int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi;
92897 float gain;
92898 if (MA_DR_MP3_HDR_TEST_MPEG1(hdr))
92899 {
92900 static const ma_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 };
92901 int part = g_scfc_decode[gr->scalefac_compress];
92902 scf_size[1] = scf_size[0] = (ma_uint8)(part >> 2);
92903 scf_size[3] = scf_size[2] = (ma_uint8)(part & 3);
92904 } else
92905 {
92906 static const ma_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 };
92907 int k, modprod, sfc, ist = MA_DR_MP3_HDR_TEST_I_STEREO(hdr) && ch;
92908 sfc = gr->scalefac_compress >> ist;
92909 for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4)
92910 {
92911 for (modprod = 1, i = 3; i >= 0; i--)
92912 {
92913 scf_size[i] = (ma_uint8)(sfc / modprod % g_mod[k + i]);
92914 modprod *= g_mod[k + i];
92915 }
92916 }
92917 scf_partition += k;
92918 scfsi = -16;
92919 }
92920 ma_dr_mp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi);
92921 if (gr->n_short_sfb)
92922 {
92923 int sh = 3 - scf_shift;
92924 for (i = 0; i < gr->n_short_sfb; i += 3)
92925 {
92926 iscf[gr->n_long_sfb + i + 0] = (ma_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh));
92927 iscf[gr->n_long_sfb + i + 1] = (ma_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh));
92928 iscf[gr->n_long_sfb + i + 2] = (ma_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh));
92929 }
92930 } else if (gr->preflag)
92931 {
92932 static const ma_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 };
92933 for (i = 0; i < 10; i++)
92934 {
92935 iscf[11 + i] = (ma_uint8)(iscf[11 + i] + g_preamp[i]);
92936 }
92937 }
92938 gain_exp = gr->global_gain + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210 - (MA_DR_MP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0);
92939 gain = ma_dr_mp3_L3_ldexp_q2(1 << (MA_DR_MP3_MAX_SCFI/4), MA_DR_MP3_MAX_SCFI - gain_exp);
92940 for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++)
92941 {
92942 scf[i] = ma_dr_mp3_L3_ldexp_q2(gain, iscf[i] << scf_shift);
92943 }
92944}
92945static const float ma_dr_mp3_g_pow43[129 + 16] = {
92946 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,
92947 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
92948};
92949static float ma_dr_mp3_L3_pow_43(int x)
92950{
92951 float frac;
92952 int sign, mult = 256;
92953 if (x < 129)
92954 {
92955 return ma_dr_mp3_g_pow43[16 + x];
92956 }
92957 if (x < 1024)
92958 {
92959 mult = 16;
92960 x <<= 3;
92961 }
92962 sign = 2*x & 64;
92963 frac = (float)((x & 63) - sign) / ((x & ~63) + sign);
92964 return ma_dr_mp3_g_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult;
92965}
92966static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit)
92967{
92968 static const ma_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
92969 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,
92970 -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,
92971 -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,
92972 -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,
92973 -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,
92974 -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,
92975 -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,
92976 -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,
92977 -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,
92978 -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,
92979 -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,
92980 -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,
92981 -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,
92982 -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,
92983 -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 };
92984 static const ma_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205};
92985 static const ma_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 };
92986 static const ma_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 };
92987 static const ma_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 };
92988#define MA_DR_MP3_PEEK_BITS(n) (bs_cache >> (32 - (n)))
92989#define MA_DR_MP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); }
92990#define MA_DR_MP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (ma_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; }
92991#define MA_DR_MP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh)
92992 float one = 0.0f;
92993 int ireg = 0, big_val_cnt = gr_info->big_values;
92994 const ma_uint8 *sfb = gr_info->sfbtab;
92995 const ma_uint8 *bs_next_ptr = bs->buf + bs->pos/8;
92996 ma_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7);
92997 int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8;
92998 bs_next_ptr += 4;
92999 while (big_val_cnt > 0)
93000 {
93001 int tab_num = gr_info->table_select[ireg];
93002 int sfb_cnt = gr_info->region_count[ireg++];
93003 const ma_int16 *codebook = tabs + tabindex[tab_num];
93004 int linbits = g_linbits[tab_num];
93005 if (linbits)
93006 {
93007 do
93008 {
93009 np = *sfb++ / 2;
93010 pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np);
93011 one = *scf++;
93012 do
93013 {
93014 int j, w = 5;
93015 int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)];
93016 while (leaf < 0)
93017 {
93018 MA_DR_MP3_FLUSH_BITS(w);
93019 w = leaf & 7;
93020 leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)];
93021 }
93022 MA_DR_MP3_FLUSH_BITS(leaf >> 8);
93023 for (j = 0; j < 2; j++, dst++, leaf >>= 4)
93024 {
93025 int lsb = leaf & 0x0F;
93026 if (lsb == 15)
93027 {
93028 lsb += MA_DR_MP3_PEEK_BITS(linbits);
93029 MA_DR_MP3_FLUSH_BITS(linbits);
93030 MA_DR_MP3_CHECK_BITS;
93031 *dst = one*ma_dr_mp3_L3_pow_43(lsb)*((ma_int32)bs_cache < 0 ? -1: 1);
93032 } else
93033 {
93034 *dst = ma_dr_mp3_g_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
93035 }
93036 MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0);
93037 }
93038 MA_DR_MP3_CHECK_BITS;
93039 } while (--pairs_to_decode);
93040 } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
93041 } else
93042 {
93043 do
93044 {
93045 np = *sfb++ / 2;
93046 pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np);
93047 one = *scf++;
93048 do
93049 {
93050 int j, w = 5;
93051 int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)];
93052 while (leaf < 0)
93053 {
93054 MA_DR_MP3_FLUSH_BITS(w);
93055 w = leaf & 7;
93056 leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)];
93057 }
93058 MA_DR_MP3_FLUSH_BITS(leaf >> 8);
93059 for (j = 0; j < 2; j++, dst++, leaf >>= 4)
93060 {
93061 int lsb = leaf & 0x0F;
93062 *dst = ma_dr_mp3_g_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
93063 MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0);
93064 }
93065 MA_DR_MP3_CHECK_BITS;
93066 } while (--pairs_to_decode);
93067 } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
93068 }
93069 }
93070 for (np = 1 - big_val_cnt;; dst += 4)
93071 {
93072 const ma_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32;
93073 int leaf = codebook_count1[MA_DR_MP3_PEEK_BITS(4)];
93074 if (!(leaf & 8))
93075 {
93076 leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))];
93077 }
93078 MA_DR_MP3_FLUSH_BITS(leaf & 7);
93079 if (MA_DR_MP3_BSPOS > layer3gr_limit)
93080 {
93081 break;
93082 }
93083#define MA_DR_MP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; }
93084#define MA_DR_MP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((ma_int32)bs_cache < 0) ? -one : one; MA_DR_MP3_FLUSH_BITS(1) }
93085 MA_DR_MP3_RELOAD_SCALEFACTOR;
93086 MA_DR_MP3_DEQ_COUNT1(0);
93087 MA_DR_MP3_DEQ_COUNT1(1);
93088 MA_DR_MP3_RELOAD_SCALEFACTOR;
93089 MA_DR_MP3_DEQ_COUNT1(2);
93090 MA_DR_MP3_DEQ_COUNT1(3);
93091 MA_DR_MP3_CHECK_BITS;
93092 }
93093 bs->pos = layer3gr_limit;
93094}
93095static void ma_dr_mp3_L3_midside_stereo(float *left, int n)
93096{
93097 int i = 0;
93098 float *right = left + 576;
93099#if MA_DR_MP3_HAVE_SIMD
93100 if (ma_dr_mp3_have_simd())
93101 {
93102 for (; i < n - 3; i += 4)
93103 {
93104 ma_dr_mp3_f4 vl = MA_DR_MP3_VLD(left + i);
93105 ma_dr_mp3_f4 vr = MA_DR_MP3_VLD(right + i);
93106 MA_DR_MP3_VSTORE(left + i, MA_DR_MP3_VADD(vl, vr));
93107 MA_DR_MP3_VSTORE(right + i, MA_DR_MP3_VSUB(vl, vr));
93108 }
93109#ifdef __GNUC__
93110 if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0)
93111 return;
93112#endif
93113 }
93114#endif
93115 for (; i < n; i++)
93116 {
93117 float a = left[i];
93118 float b = right[i];
93119 left[i] = a + b;
93120 right[i] = a - b;
93121 }
93122}
93123static void ma_dr_mp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr)
93124{
93125 int i;
93126 for (i = 0; i < n; i++)
93127 {
93128 left[i + 576] = left[i]*kr;
93129 left[i] = left[i]*kl;
93130 }
93131}
93132static void ma_dr_mp3_L3_stereo_top_band(const float *right, const ma_uint8 *sfb, int nbands, int max_band[3])
93133{
93134 int i, k;
93135 max_band[0] = max_band[1] = max_band[2] = -1;
93136 for (i = 0; i < nbands; i++)
93137 {
93138 for (k = 0; k < sfb[i]; k += 2)
93139 {
93140 if (right[k] != 0 || right[k + 1] != 0)
93141 {
93142 max_band[i % 3] = i;
93143 break;
93144 }
93145 }
93146 right += sfb[i];
93147 }
93148}
93149static void ma_dr_mp3_L3_stereo_process(float *left, const ma_uint8 *ist_pos, const ma_uint8 *sfb, const ma_uint8 *hdr, int max_band[3], int mpeg2_sh)
93150{
93151 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 };
93152 unsigned i, max_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 7 : 64;
93153 for (i = 0; sfb[i]; i++)
93154 {
93155 unsigned ipos = ist_pos[i];
93156 if ((int)i > max_band[i % 3] && ipos < max_pos)
93157 {
93158 float kl, kr, s = MA_DR_MP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1;
93159 if (MA_DR_MP3_HDR_TEST_MPEG1(hdr))
93160 {
93161 kl = g_pan[2*ipos];
93162 kr = g_pan[2*ipos + 1];
93163 } else
93164 {
93165 kl = 1;
93166 kr = ma_dr_mp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh);
93167 if (ipos & 1)
93168 {
93169 kl = kr;
93170 kr = 1;
93171 }
93172 }
93173 ma_dr_mp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s);
93174 } else if (MA_DR_MP3_HDR_TEST_MS_STEREO(hdr))
93175 {
93176 ma_dr_mp3_L3_midside_stereo(left, sfb[i]);
93177 }
93178 left += sfb[i];
93179 }
93180}
93181static void ma_dr_mp3_L3_intensity_stereo(float *left, ma_uint8 *ist_pos, const ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr)
93182{
93183 int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb;
93184 int i, max_blocks = gr->n_short_sfb ? 3 : 1;
93185 ma_dr_mp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band);
93186 if (gr->n_long_sfb)
93187 {
93188 max_band[0] = max_band[1] = max_band[2] = MA_DR_MP3_MAX(MA_DR_MP3_MAX(max_band[0], max_band[1]), max_band[2]);
93189 }
93190 for (i = 0; i < max_blocks; i++)
93191 {
93192 int default_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 3 : 0;
93193 int itop = n_sfb - max_blocks + i;
93194 int prev = itop - max_blocks;
93195 ist_pos[itop] = (ma_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]);
93196 }
93197 ma_dr_mp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1);
93198}
93199static void ma_dr_mp3_L3_reorder(float *grbuf, float *scratch, const ma_uint8 *sfb)
93200{
93201 int i, len;
93202 float *src = grbuf, *dst = scratch;
93203 for (;0 != (len = *sfb); sfb += 3, src += 2*len)
93204 {
93205 for (i = 0; i < len; i++, src++)
93206 {
93207 *dst++ = src[0*len];
93208 *dst++ = src[1*len];
93209 *dst++ = src[2*len];
93210 }
93211 }
93212 MA_DR_MP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float));
93213}
93214static void ma_dr_mp3_L3_antialias(float *grbuf, int nbands)
93215{
93216 static const float g_aa[2][8] = {
93217 {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f},
93218 {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f}
93219 };
93220 for (; nbands > 0; nbands--, grbuf += 18)
93221 {
93222 int i = 0;
93223#if MA_DR_MP3_HAVE_SIMD
93224 if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4)
93225 {
93226 ma_dr_mp3_f4 vu = MA_DR_MP3_VLD(grbuf + 18 + i);
93227 ma_dr_mp3_f4 vd = MA_DR_MP3_VLD(grbuf + 14 - i);
93228 ma_dr_mp3_f4 vc0 = MA_DR_MP3_VLD(g_aa[0] + i);
93229 ma_dr_mp3_f4 vc1 = MA_DR_MP3_VLD(g_aa[1] + i);
93230 vd = MA_DR_MP3_VREV(vd);
93231 MA_DR_MP3_VSTORE(grbuf + 18 + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vu, vc0), MA_DR_MP3_VMUL(vd, vc1)));
93232 vd = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vu, vc1), MA_DR_MP3_VMUL(vd, vc0));
93233 MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vd));
93234 }
93235#endif
93236#ifndef MA_DR_MP3_ONLY_SIMD
93237 for(; i < 8; i++)
93238 {
93239 float u = grbuf[18 + i];
93240 float d = grbuf[17 - i];
93241 grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i];
93242 grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i];
93243 }
93244#endif
93245 }
93246}
93247static void ma_dr_mp3_L3_dct3_9(float *y)
93248{
93249 float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4;
93250 s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8];
93251 t0 = s0 + s6*0.5f;
93252 s0 -= s6;
93253 t4 = (s4 + s2)*0.93969262f;
93254 t2 = (s8 + s2)*0.76604444f;
93255 s6 = (s4 - s8)*0.17364818f;
93256 s4 += s8 - s2;
93257 s2 = s0 - s4*0.5f;
93258 y[4] = s4 + s0;
93259 s8 = t0 - t2 + s6;
93260 s0 = t0 - t4 + t2;
93261 s4 = t0 + t4 - s6;
93262 s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7];
93263 s3 *= 0.86602540f;
93264 t0 = (s5 + s1)*0.98480775f;
93265 t4 = (s5 - s7)*0.34202014f;
93266 t2 = (s1 + s7)*0.64278761f;
93267 s1 = (s1 - s5 - s7)*0.86602540f;
93268 s5 = t0 - s3 - t2;
93269 s7 = t4 - s3 - t0;
93270 s3 = t4 + s3 - t2;
93271 y[0] = s4 - s7;
93272 y[1] = s2 + s1;
93273 y[2] = s0 - s3;
93274 y[3] = s8 + s5;
93275 y[5] = s8 - s5;
93276 y[6] = s0 + s3;
93277 y[7] = s2 - s1;
93278 y[8] = s4 + s7;
93279}
93280static void ma_dr_mp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands)
93281{
93282 int i, j;
93283 static const float g_twid9[18] = {
93284 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
93285 };
93286 for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9)
93287 {
93288 float co[9], si[9];
93289 co[0] = -grbuf[0];
93290 si[0] = grbuf[17];
93291 for (i = 0; i < 4; i++)
93292 {
93293 si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2];
93294 co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2];
93295 si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3];
93296 co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]);
93297 }
93298 ma_dr_mp3_L3_dct3_9(co);
93299 ma_dr_mp3_L3_dct3_9(si);
93300 si[1] = -si[1];
93301 si[3] = -si[3];
93302 si[5] = -si[5];
93303 si[7] = -si[7];
93304 i = 0;
93305#if MA_DR_MP3_HAVE_SIMD
93306 if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4)
93307 {
93308 ma_dr_mp3_f4 vovl = MA_DR_MP3_VLD(overlap + i);
93309 ma_dr_mp3_f4 vc = MA_DR_MP3_VLD(co + i);
93310 ma_dr_mp3_f4 vs = MA_DR_MP3_VLD(si + i);
93311 ma_dr_mp3_f4 vr0 = MA_DR_MP3_VLD(g_twid9 + i);
93312 ma_dr_mp3_f4 vr1 = MA_DR_MP3_VLD(g_twid9 + 9 + i);
93313 ma_dr_mp3_f4 vw0 = MA_DR_MP3_VLD(window + i);
93314 ma_dr_mp3_f4 vw1 = MA_DR_MP3_VLD(window + 9 + i);
93315 ma_dr_mp3_f4 vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vc, vr1), MA_DR_MP3_VMUL(vs, vr0));
93316 MA_DR_MP3_VSTORE(overlap + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vc, vr0), MA_DR_MP3_VMUL(vs, vr1)));
93317 MA_DR_MP3_VSTORE(grbuf + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vovl, vw0), MA_DR_MP3_VMUL(vsum, vw1)));
93318 vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vovl, vw1), MA_DR_MP3_VMUL(vsum, vw0));
93319 MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vsum));
93320 }
93321#endif
93322 for (; i < 9; i++)
93323 {
93324 float ovl = overlap[i];
93325 float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i];
93326 overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i];
93327 grbuf[i] = ovl*window[0 + i] - sum*window[9 + i];
93328 grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i];
93329 }
93330 }
93331}
93332static void ma_dr_mp3_L3_idct3(float x0, float x1, float x2, float *dst)
93333{
93334 float m1 = x1*0.86602540f;
93335 float a1 = x0 - x2*0.5f;
93336 dst[1] = x0 + x2;
93337 dst[0] = a1 + m1;
93338 dst[2] = a1 - m1;
93339}
93340static void ma_dr_mp3_L3_imdct12(float *x, float *dst, float *overlap)
93341{
93342 static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f };
93343 float co[3], si[3];
93344 int i;
93345 ma_dr_mp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co);
93346 ma_dr_mp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si);
93347 si[1] = -si[1];
93348 for (i = 0; i < 3; i++)
93349 {
93350 float ovl = overlap[i];
93351 float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i];
93352 overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i];
93353 dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i];
93354 dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i];
93355 }
93356}
93357static void ma_dr_mp3_L3_imdct_short(float *grbuf, float *overlap, int nbands)
93358{
93359 for (;nbands > 0; nbands--, overlap += 9, grbuf += 18)
93360 {
93361 float tmp[18];
93362 MA_DR_MP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp));
93363 MA_DR_MP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float));
93364 ma_dr_mp3_L3_imdct12(tmp, grbuf + 6, overlap + 6);
93365 ma_dr_mp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6);
93366 ma_dr_mp3_L3_imdct12(tmp + 2, overlap, overlap + 6);
93367 }
93368}
93369static void ma_dr_mp3_L3_change_sign(float *grbuf)
93370{
93371 int b, i;
93372 for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36)
93373 for (i = 1; i < 18; i += 2)
93374 grbuf[i] = -grbuf[i];
93375}
93376static void ma_dr_mp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands)
93377{
93378 static const float g_mdct_window[2][18] = {
93379 { 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 },
93380 { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f }
93381 };
93382 if (n_long_bands)
93383 {
93384 ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands);
93385 grbuf += 18*n_long_bands;
93386 overlap += 9*n_long_bands;
93387 }
93388 if (block_type == MA_DR_MP3_SHORT_BLOCK_TYPE)
93389 ma_dr_mp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands);
93390 else
93391 ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == MA_DR_MP3_STOP_BLOCK_TYPE], 32 - n_long_bands);
93392}
93393static void ma_dr_mp3_L3_save_reservoir(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s)
93394{
93395 int pos = (s->bs.pos + 7)/8u;
93396 int remains = s->bs.limit/8u - pos;
93397 if (remains > MA_DR_MP3_MAX_BITRESERVOIR_BYTES)
93398 {
93399 pos += remains - MA_DR_MP3_MAX_BITRESERVOIR_BYTES;
93400 remains = MA_DR_MP3_MAX_BITRESERVOIR_BYTES;
93401 }
93402 if (remains > 0)
93403 {
93404 MA_DR_MP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains);
93405 }
93406 h->reserv = remains;
93407}
93408static int ma_dr_mp3_L3_restore_reservoir(ma_dr_mp3dec *h, ma_dr_mp3_bs *bs, ma_dr_mp3dec_scratch *s, int main_data_begin)
93409{
93410 int frame_bytes = (bs->limit - bs->pos)/8;
93411 int bytes_have = MA_DR_MP3_MIN(h->reserv, main_data_begin);
93412 MA_DR_MP3_COPY_MEMORY(s->maindata, h->reserv_buf + MA_DR_MP3_MAX(0, h->reserv - main_data_begin), MA_DR_MP3_MIN(h->reserv, main_data_begin));
93413 MA_DR_MP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes);
93414 ma_dr_mp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes);
93415 return h->reserv >= main_data_begin;
93416}
93417static void ma_dr_mp3_L3_decode(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s, ma_dr_mp3_L3_gr_info *gr_info, int nch)
93418{
93419 int ch;
93420 for (ch = 0; ch < nch; ch++)
93421 {
93422 int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length;
93423 ma_dr_mp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch);
93424 ma_dr_mp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit);
93425 }
93426 if (MA_DR_MP3_HDR_TEST_I_STEREO(h->header))
93427 {
93428 ma_dr_mp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header);
93429 } else if (MA_DR_MP3_HDR_IS_MS_STEREO(h->header))
93430 {
93431 ma_dr_mp3_L3_midside_stereo(s->grbuf[0], 576);
93432 }
93433 for (ch = 0; ch < nch; ch++, gr_info++)
93434 {
93435 int aa_bands = 31;
93436 int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2);
93437 if (gr_info->n_short_sfb)
93438 {
93439 aa_bands = n_long_bands - 1;
93440 ma_dr_mp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb);
93441 }
93442 ma_dr_mp3_L3_antialias(s->grbuf[ch], aa_bands);
93443 ma_dr_mp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands);
93444 ma_dr_mp3_L3_change_sign(s->grbuf[ch]);
93445 }
93446}
93447static void ma_dr_mp3d_DCT_II(float *grbuf, int n)
93448{
93449 static const float g_sec[24] = {
93450 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
93451 };
93452 int i, k = 0;
93453#if MA_DR_MP3_HAVE_SIMD
93454 if (ma_dr_mp3_have_simd()) for (; k < n; k += 4)
93455 {
93456 ma_dr_mp3_f4 t[4][8], *x;
93457 float *y = grbuf + k;
93458 for (x = t[0], i = 0; i < 8; i++, x++)
93459 {
93460 ma_dr_mp3_f4 x0 = MA_DR_MP3_VLD(&y[i*18]);
93461 ma_dr_mp3_f4 x1 = MA_DR_MP3_VLD(&y[(15 - i)*18]);
93462 ma_dr_mp3_f4 x2 = MA_DR_MP3_VLD(&y[(16 + i)*18]);
93463 ma_dr_mp3_f4 x3 = MA_DR_MP3_VLD(&y[(31 - i)*18]);
93464 ma_dr_mp3_f4 t0 = MA_DR_MP3_VADD(x0, x3);
93465 ma_dr_mp3_f4 t1 = MA_DR_MP3_VADD(x1, x2);
93466 ma_dr_mp3_f4 t2 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x1, x2), g_sec[3*i + 0]);
93467 ma_dr_mp3_f4 t3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x3), g_sec[3*i + 1]);
93468 x[0] = MA_DR_MP3_VADD(t0, t1);
93469 x[8] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t0, t1), g_sec[3*i + 2]);
93470 x[16] = MA_DR_MP3_VADD(t3, t2);
93471 x[24] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t3, t2), g_sec[3*i + 2]);
93472 }
93473 for (x = t[0], i = 0; i < 4; i++, x += 8)
93474 {
93475 ma_dr_mp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt;
93476 xt = MA_DR_MP3_VSUB(x0, x7); x0 = MA_DR_MP3_VADD(x0, x7);
93477 x7 = MA_DR_MP3_VSUB(x1, x6); x1 = MA_DR_MP3_VADD(x1, x6);
93478 x6 = MA_DR_MP3_VSUB(x2, x5); x2 = MA_DR_MP3_VADD(x2, x5);
93479 x5 = MA_DR_MP3_VSUB(x3, x4); x3 = MA_DR_MP3_VADD(x3, x4);
93480 x4 = MA_DR_MP3_VSUB(x0, x3); x0 = MA_DR_MP3_VADD(x0, x3);
93481 x3 = MA_DR_MP3_VSUB(x1, x2); x1 = MA_DR_MP3_VADD(x1, x2);
93482 x[0] = MA_DR_MP3_VADD(x0, x1);
93483 x[4] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x1), 0.70710677f);
93484 x5 = MA_DR_MP3_VADD(x5, x6);
93485 x6 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x6, x7), 0.70710677f);
93486 x7 = MA_DR_MP3_VADD(x7, xt);
93487 x3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x3, x4), 0.70710677f);
93488 x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f));
93489 x7 = MA_DR_MP3_VADD(x7, MA_DR_MP3_VMUL_S(x5, 0.382683432f));
93490 x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f));
93491 x0 = MA_DR_MP3_VSUB(xt, x6); xt = MA_DR_MP3_VADD(xt, x6);
93492 x[1] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(xt, x7), 0.50979561f);
93493 x[2] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x4, x3), 0.54119611f);
93494 x[3] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x5), 0.60134488f);
93495 x[5] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x0, x5), 0.89997619f);
93496 x[6] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x4, x3), 1.30656302f);
93497 x[7] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(xt, x7), 2.56291556f);
93498 }
93499 if (k > n - 3)
93500 {
93501#if MA_DR_MP3_HAVE_SSE
93502#define MA_DR_MP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v)
93503#else
93504#define MA_DR_MP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v))
93505#endif
93506 for (i = 0; i < 7; i++, y += 4*18)
93507 {
93508 ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]);
93509 MA_DR_MP3_VSAVE2(0, t[0][i]);
93510 MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][i], s));
93511 MA_DR_MP3_VSAVE2(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1]));
93512 MA_DR_MP3_VSAVE2(3, MA_DR_MP3_VADD(t[2][1 + i], s));
93513 }
93514 MA_DR_MP3_VSAVE2(0, t[0][7]);
93515 MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][7], t[3][7]));
93516 MA_DR_MP3_VSAVE2(2, t[1][7]);
93517 MA_DR_MP3_VSAVE2(3, t[3][7]);
93518 } else
93519 {
93520#define MA_DR_MP3_VSAVE4(i, v) MA_DR_MP3_VSTORE(&y[(i)*18], v)
93521 for (i = 0; i < 7; i++, y += 4*18)
93522 {
93523 ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]);
93524 MA_DR_MP3_VSAVE4(0, t[0][i]);
93525 MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][i], s));
93526 MA_DR_MP3_VSAVE4(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1]));
93527 MA_DR_MP3_VSAVE4(3, MA_DR_MP3_VADD(t[2][1 + i], s));
93528 }
93529 MA_DR_MP3_VSAVE4(0, t[0][7]);
93530 MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][7], t[3][7]));
93531 MA_DR_MP3_VSAVE4(2, t[1][7]);
93532 MA_DR_MP3_VSAVE4(3, t[3][7]);
93533 }
93534 } else
93535#endif
93536#ifdef MA_DR_MP3_ONLY_SIMD
93537 {}
93538#else
93539 for (; k < n; k++)
93540 {
93541 float t[4][8], *x, *y = grbuf + k;
93542 for (x = t[0], i = 0; i < 8; i++, x++)
93543 {
93544 float x0 = y[i*18];
93545 float x1 = y[(15 - i)*18];
93546 float x2 = y[(16 + i)*18];
93547 float x3 = y[(31 - i)*18];
93548 float t0 = x0 + x3;
93549 float t1 = x1 + x2;
93550 float t2 = (x1 - x2)*g_sec[3*i + 0];
93551 float t3 = (x0 - x3)*g_sec[3*i + 1];
93552 x[0] = t0 + t1;
93553 x[8] = (t0 - t1)*g_sec[3*i + 2];
93554 x[16] = t3 + t2;
93555 x[24] = (t3 - t2)*g_sec[3*i + 2];
93556 }
93557 for (x = t[0], i = 0; i < 4; i++, x += 8)
93558 {
93559 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;
93560 xt = x0 - x7; x0 += x7;
93561 x7 = x1 - x6; x1 += x6;
93562 x6 = x2 - x5; x2 += x5;
93563 x5 = x3 - x4; x3 += x4;
93564 x4 = x0 - x3; x0 += x3;
93565 x3 = x1 - x2; x1 += x2;
93566 x[0] = x0 + x1;
93567 x[4] = (x0 - x1)*0.70710677f;
93568 x5 = x5 + x6;
93569 x6 = (x6 + x7)*0.70710677f;
93570 x7 = x7 + xt;
93571 x3 = (x3 + x4)*0.70710677f;
93572 x5 -= x7*0.198912367f;
93573 x7 += x5*0.382683432f;
93574 x5 -= x7*0.198912367f;
93575 x0 = xt - x6; xt += x6;
93576 x[1] = (xt + x7)*0.50979561f;
93577 x[2] = (x4 + x3)*0.54119611f;
93578 x[3] = (x0 - x5)*0.60134488f;
93579 x[5] = (x0 + x5)*0.89997619f;
93580 x[6] = (x4 - x3)*1.30656302f;
93581 x[7] = (xt - x7)*2.56291556f;
93582 }
93583 for (i = 0; i < 7; i++, y += 4*18)
93584 {
93585 y[0*18] = t[0][i];
93586 y[1*18] = t[2][i] + t[3][i] + t[3][i + 1];
93587 y[2*18] = t[1][i] + t[1][i + 1];
93588 y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1];
93589 }
93590 y[0*18] = t[0][7];
93591 y[1*18] = t[2][7] + t[3][7];
93592 y[2*18] = t[1][7];
93593 y[3*18] = t[3][7];
93594 }
93595#endif
93596}
93597#ifndef MA_DR_MP3_FLOAT_OUTPUT
93598typedef ma_int16 ma_dr_mp3d_sample_t;
93599static ma_int16 ma_dr_mp3d_scale_pcm(float sample)
93600{
93601 ma_int16 s;
93602#if MA_DR_MP3_HAVE_ARMV6
93603 ma_int32 s32 = (ma_int32)(sample + .5f);
93604 s32 -= (s32 < 0);
93605 s = (ma_int16)ma_dr_mp3_clip_int16_arm(s32);
93606#else
93607 if (sample >= 32766.5f) return (ma_int16) 32767;
93608 if (sample <= -32767.5f) return (ma_int16)-32768;
93609 s = (ma_int16)(sample + .5f);
93610 s -= (s < 0);
93611#endif
93612 return s;
93613}
93614#else
93615typedef float ma_dr_mp3d_sample_t;
93616static float ma_dr_mp3d_scale_pcm(float sample)
93617{
93618 return sample*(1.f/32768.f);
93619}
93620#endif
93621static void ma_dr_mp3d_synth_pair(ma_dr_mp3d_sample_t *pcm, int nch, const float *z)
93622{
93623 float a;
93624 a = (z[14*64] - z[ 0]) * 29;
93625 a += (z[ 1*64] + z[13*64]) * 213;
93626 a += (z[12*64] - z[ 2*64]) * 459;
93627 a += (z[ 3*64] + z[11*64]) * 2037;
93628 a += (z[10*64] - z[ 4*64]) * 5153;
93629 a += (z[ 5*64] + z[ 9*64]) * 6574;
93630 a += (z[ 8*64] - z[ 6*64]) * 37489;
93631 a += z[ 7*64] * 75038;
93632 pcm[0] = ma_dr_mp3d_scale_pcm(a);
93633 z += 2;
93634 a = z[14*64] * 104;
93635 a += z[12*64] * 1567;
93636 a += z[10*64] * 9727;
93637 a += z[ 8*64] * 64019;
93638 a += z[ 6*64] * -9975;
93639 a += z[ 4*64] * -45;
93640 a += z[ 2*64] * 146;
93641 a += z[ 0*64] * -5;
93642 pcm[16*nch] = ma_dr_mp3d_scale_pcm(a);
93643}
93644static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, float *lins)
93645{
93646 int i;
93647 float *xr = xl + 576*(nch - 1);
93648 ma_dr_mp3d_sample_t *dstr = dstl + (nch - 1);
93649 static const float g_win[] = {
93650 -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992,
93651 -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856,
93652 -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630,
93653 -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313,
93654 -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908,
93655 -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415,
93656 -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835,
93657 -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169,
93658 -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420,
93659 -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590,
93660 -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679,
93661 -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692,
93662 -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629,
93663 -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494,
93664 -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290
93665 };
93666 float *zlin = lins + 15*64;
93667 const float *w = g_win;
93668 zlin[4*15] = xl[18*16];
93669 zlin[4*15 + 1] = xr[18*16];
93670 zlin[4*15 + 2] = xl[0];
93671 zlin[4*15 + 3] = xr[0];
93672 zlin[4*31] = xl[1 + 18*16];
93673 zlin[4*31 + 1] = xr[1 + 18*16];
93674 zlin[4*31 + 2] = xl[1];
93675 zlin[4*31 + 3] = xr[1];
93676 ma_dr_mp3d_synth_pair(dstr, nch, lins + 4*15 + 1);
93677 ma_dr_mp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1);
93678 ma_dr_mp3d_synth_pair(dstl, nch, lins + 4*15);
93679 ma_dr_mp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64);
93680#if MA_DR_MP3_HAVE_SIMD
93681 if (ma_dr_mp3_have_simd()) for (i = 14; i >= 0; i--)
93682 {
93683#define MA_DR_MP3_VLOAD(k) ma_dr_mp3_f4 w0 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 w1 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 vz = MA_DR_MP3_VLD(&zlin[4*i - 64*k]); ma_dr_mp3_f4 vy = MA_DR_MP3_VLD(&zlin[4*i - 64*(15 - k)]);
93684#define MA_DR_MP3_V0(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0)) ; a = MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1)); }
93685#define MA_DR_MP3_V1(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1))); }
93686#define MA_DR_MP3_V2(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vy, w1), MA_DR_MP3_VMUL(vz, w0))); }
93687 ma_dr_mp3_f4 a, b;
93688 zlin[4*i] = xl[18*(31 - i)];
93689 zlin[4*i + 1] = xr[18*(31 - i)];
93690 zlin[4*i + 2] = xl[1 + 18*(31 - i)];
93691 zlin[4*i + 3] = xr[1 + 18*(31 - i)];
93692 zlin[4*i + 64] = xl[1 + 18*(1 + i)];
93693 zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)];
93694 zlin[4*i - 64 + 2] = xl[18*(1 + i)];
93695 zlin[4*i - 64 + 3] = xr[18*(1 + i)];
93696 MA_DR_MP3_V0(0) MA_DR_MP3_V2(1) MA_DR_MP3_V1(2) MA_DR_MP3_V2(3) MA_DR_MP3_V1(4) MA_DR_MP3_V2(5) MA_DR_MP3_V1(6) MA_DR_MP3_V2(7)
93697 {
93698#ifndef MA_DR_MP3_FLOAT_OUTPUT
93699#if MA_DR_MP3_HAVE_SSE
93700 static const ma_dr_mp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
93701 static const ma_dr_mp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
93702 __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
93703 _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
93704 dstr[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 1);
93705 dstr[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 5);
93706 dstl[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 0);
93707 dstl[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 4);
93708 dstr[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 3);
93709 dstr[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 7);
93710 dstl[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 2);
93711 dstl[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 6);
93712#else
93713 int16x4_t pcma, pcmb;
93714 a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f));
93715 b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f));
93716 pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0)))));
93717 pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0)))));
93718 vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1);
93719 vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1);
93720 vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0);
93721 vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0);
93722 vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3);
93723 vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3);
93724 vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2);
93725 vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2);
93726#endif
93727#else
93728 #if MA_DR_MP3_HAVE_SSE
93729 static const ma_dr_mp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f };
93730 #else
93731 const ma_dr_mp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f);
93732 #endif
93733 a = MA_DR_MP3_VMUL(a, g_scale);
93734 b = MA_DR_MP3_VMUL(b, g_scale);
93735#if MA_DR_MP3_HAVE_SSE
93736 _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)));
93737 _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1)));
93738 _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)));
93739 _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0)));
93740 _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)));
93741 _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3)));
93742 _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)));
93743 _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2)));
93744#else
93745 vst1q_lane_f32(dstr + (15 - i)*nch, a, 1);
93746 vst1q_lane_f32(dstr + (17 + i)*nch, b, 1);
93747 vst1q_lane_f32(dstl + (15 - i)*nch, a, 0);
93748 vst1q_lane_f32(dstl + (17 + i)*nch, b, 0);
93749 vst1q_lane_f32(dstr + (47 - i)*nch, a, 3);
93750 vst1q_lane_f32(dstr + (49 + i)*nch, b, 3);
93751 vst1q_lane_f32(dstl + (47 - i)*nch, a, 2);
93752 vst1q_lane_f32(dstl + (49 + i)*nch, b, 2);
93753#endif
93754#endif
93755 }
93756 } else
93757#endif
93758#ifdef MA_DR_MP3_ONLY_SIMD
93759 {}
93760#else
93761 for (i = 14; i >= 0; i--)
93762 {
93763#define MA_DR_MP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64];
93764#define MA_DR_MP3_S0(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; }
93765#define MA_DR_MP3_S1(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; }
93766#define MA_DR_MP3_S2(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; }
93767 float a[4], b[4];
93768 zlin[4*i] = xl[18*(31 - i)];
93769 zlin[4*i + 1] = xr[18*(31 - i)];
93770 zlin[4*i + 2] = xl[1 + 18*(31 - i)];
93771 zlin[4*i + 3] = xr[1 + 18*(31 - i)];
93772 zlin[4*(i + 16)] = xl[1 + 18*(1 + i)];
93773 zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)];
93774 zlin[4*(i - 16) + 2] = xl[18*(1 + i)];
93775 zlin[4*(i - 16) + 3] = xr[18*(1 + i)];
93776 MA_DR_MP3_S0(0) MA_DR_MP3_S2(1) MA_DR_MP3_S1(2) MA_DR_MP3_S2(3) MA_DR_MP3_S1(4) MA_DR_MP3_S2(5) MA_DR_MP3_S1(6) MA_DR_MP3_S2(7)
93777 dstr[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[1]);
93778 dstr[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[1]);
93779 dstl[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[0]);
93780 dstl[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[0]);
93781 dstr[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[3]);
93782 dstr[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[3]);
93783 dstl[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[2]);
93784 dstl[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[2]);
93785 }
93786#endif
93787}
93788static void ma_dr_mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, ma_dr_mp3d_sample_t *pcm, float *lins)
93789{
93790 int i;
93791 for (i = 0; i < nch; i++)
93792 {
93793 ma_dr_mp3d_DCT_II(grbuf + 576*i, nbands);
93794 }
93795 MA_DR_MP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64);
93796 for (i = 0; i < nbands; i += 2)
93797 {
93798 ma_dr_mp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64);
93799 }
93800#ifndef MA_DR_MP3_NONSTANDARD_BUT_LOGICAL
93801 if (nch == 1)
93802 {
93803 for (i = 0; i < 15*64; i += 2)
93804 {
93805 qmf_state[i] = lins[nbands*64 + i];
93806 }
93807 } else
93808#endif
93809 {
93810 MA_DR_MP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64);
93811 }
93812}
93813static int ma_dr_mp3d_match_frame(const ma_uint8 *hdr, int mp3_bytes, int frame_bytes)
93814{
93815 int i, nmatch;
93816 for (i = 0, nmatch = 0; nmatch < MA_DR_MP3_MAX_FRAME_SYNC_MATCHES; nmatch++)
93817 {
93818 i += ma_dr_mp3_hdr_frame_bytes(hdr + i, frame_bytes) + ma_dr_mp3_hdr_padding(hdr + i);
93819 if (i + MA_DR_MP3_HDR_SIZE > mp3_bytes)
93820 return nmatch > 0;
93821 if (!ma_dr_mp3_hdr_compare(hdr, hdr + i))
93822 return 0;
93823 }
93824 return 1;
93825}
93826static int ma_dr_mp3d_find_frame(const ma_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes)
93827{
93828 int i, k;
93829 for (i = 0; i < mp3_bytes - MA_DR_MP3_HDR_SIZE; i++, mp3++)
93830 {
93831 if (ma_dr_mp3_hdr_valid(mp3))
93832 {
93833 int frame_bytes = ma_dr_mp3_hdr_frame_bytes(mp3, *free_format_bytes);
93834 int frame_and_padding = frame_bytes + ma_dr_mp3_hdr_padding(mp3);
93835 for (k = MA_DR_MP3_HDR_SIZE; !frame_bytes && k < MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - MA_DR_MP3_HDR_SIZE; k++)
93836 {
93837 if (ma_dr_mp3_hdr_compare(mp3, mp3 + k))
93838 {
93839 int fb = k - ma_dr_mp3_hdr_padding(mp3);
93840 int nextfb = fb + ma_dr_mp3_hdr_padding(mp3 + k);
93841 if (i + k + nextfb + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + k + nextfb))
93842 continue;
93843 frame_and_padding = k;
93844 frame_bytes = fb;
93845 *free_format_bytes = fb;
93846 }
93847 }
93848 if ((frame_bytes && i + frame_and_padding <= mp3_bytes &&
93849 ma_dr_mp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) ||
93850 (!i && frame_and_padding == mp3_bytes))
93851 {
93852 *ptr_frame_bytes = frame_and_padding;
93853 return i;
93854 }
93855 *free_format_bytes = 0;
93856 }
93857 }
93858 *ptr_frame_bytes = 0;
93859 return mp3_bytes;
93860}
93861MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec)
93862{
93863 dec->header[0] = 0;
93864}
93865MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info)
93866{
93867 int i = 0, igr, frame_size = 0, success = 1;
93868 const ma_uint8 *hdr;
93869 ma_dr_mp3_bs bs_frame[1];
93870 ma_dr_mp3dec_scratch scratch;
93871 if (mp3_bytes > 4 && dec->header[0] == 0xff && ma_dr_mp3_hdr_compare(dec->header, mp3))
93872 {
93873 frame_size = ma_dr_mp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + ma_dr_mp3_hdr_padding(mp3);
93874 if (frame_size != mp3_bytes && (frame_size + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + frame_size)))
93875 {
93876 frame_size = 0;
93877 }
93878 }
93879 if (!frame_size)
93880 {
93881 MA_DR_MP3_ZERO_MEMORY(dec, sizeof(ma_dr_mp3dec));
93882 i = ma_dr_mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size);
93883 if (!frame_size || i + frame_size > mp3_bytes)
93884 {
93885 info->frame_bytes = i;
93886 return 0;
93887 }
93888 }
93889 hdr = mp3 + i;
93890 MA_DR_MP3_COPY_MEMORY(dec->header, hdr, MA_DR_MP3_HDR_SIZE);
93891 info->frame_bytes = i + frame_size;
93892 info->channels = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2;
93893 info->sample_rate = ma_dr_mp3_hdr_sample_rate_hz(hdr);
93894 info->layer = 4 - MA_DR_MP3_HDR_GET_LAYER(hdr);
93895 info->bitrate_kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr);
93896 ma_dr_mp3_bs_init(bs_frame, hdr + MA_DR_MP3_HDR_SIZE, frame_size - MA_DR_MP3_HDR_SIZE);
93897 if (MA_DR_MP3_HDR_IS_CRC(hdr))
93898 {
93899 ma_dr_mp3_bs_get_bits(bs_frame, 16);
93900 }
93901 if (info->layer == 3)
93902 {
93903 int main_data_begin = ma_dr_mp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr);
93904 if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit)
93905 {
93906 ma_dr_mp3dec_init(dec);
93907 return 0;
93908 }
93909 success = ma_dr_mp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin);
93910 if (success && pcm != NULL)
93911 {
93912 for (igr = 0; igr < (MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*576*info->channels))
93913 {
93914 MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
93915 ma_dr_mp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels);
93916 ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]);
93917 }
93918 }
93919 ma_dr_mp3_L3_save_reservoir(dec, &scratch);
93920 } else
93921 {
93922#ifdef MA_DR_MP3_ONLY_MP3
93923 return 0;
93924#else
93925 ma_dr_mp3_L12_scale_info sci[1];
93926 if (pcm == NULL) {
93927 return ma_dr_mp3_hdr_frame_samples(hdr);
93928 }
93929 ma_dr_mp3_L12_read_scale_info(hdr, bs_frame, sci);
93930 MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
93931 for (i = 0, igr = 0; igr < 3; igr++)
93932 {
93933 if (12 == (i += ma_dr_mp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1)))
93934 {
93935 i = 0;
93936 ma_dr_mp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]);
93937 ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]);
93938 MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
93939 pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*384*info->channels);
93940 }
93941 if (bs_frame->pos > bs_frame->limit)
93942 {
93943 ma_dr_mp3dec_init(dec);
93944 return 0;
93945 }
93946 }
93947#endif
93948 }
93949 return success*ma_dr_mp3_hdr_frame_samples(dec->header);
93950}
93951MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples)
93952{
93953 size_t i = 0;
93954#if MA_DR_MP3_HAVE_SIMD
93955 size_t aligned_count = num_samples & ~7;
93956 for(; i < aligned_count; i+=8)
93957 {
93958 ma_dr_mp3_f4 scale = MA_DR_MP3_VSET(32768.0f);
93959 ma_dr_mp3_f4 a = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i ]), scale);
93960 ma_dr_mp3_f4 b = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i+4]), scale);
93961#if MA_DR_MP3_HAVE_SSE
93962 ma_dr_mp3_f4 s16max = MA_DR_MP3_VSET( 32767.0f);
93963 ma_dr_mp3_f4 s16min = MA_DR_MP3_VSET(-32768.0f);
93964 __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)),
93965 _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min)));
93966 out[i ] = (ma_int16)_mm_extract_epi16(pcm8, 0);
93967 out[i+1] = (ma_int16)_mm_extract_epi16(pcm8, 1);
93968 out[i+2] = (ma_int16)_mm_extract_epi16(pcm8, 2);
93969 out[i+3] = (ma_int16)_mm_extract_epi16(pcm8, 3);
93970 out[i+4] = (ma_int16)_mm_extract_epi16(pcm8, 4);
93971 out[i+5] = (ma_int16)_mm_extract_epi16(pcm8, 5);
93972 out[i+6] = (ma_int16)_mm_extract_epi16(pcm8, 6);
93973 out[i+7] = (ma_int16)_mm_extract_epi16(pcm8, 7);
93974#else
93975 int16x4_t pcma, pcmb;
93976 a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f));
93977 b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f));
93978 pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0)))));
93979 pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0)))));
93980 vst1_lane_s16(out+i , pcma, 0);
93981 vst1_lane_s16(out+i+1, pcma, 1);
93982 vst1_lane_s16(out+i+2, pcma, 2);
93983 vst1_lane_s16(out+i+3, pcma, 3);
93984 vst1_lane_s16(out+i+4, pcmb, 0);
93985 vst1_lane_s16(out+i+5, pcmb, 1);
93986 vst1_lane_s16(out+i+6, pcmb, 2);
93987 vst1_lane_s16(out+i+7, pcmb, 3);
93988#endif
93989 }
93990#endif
93991 for(; i < num_samples; i++)
93992 {
93993 float sample = in[i] * 32768.0f;
93994 if (sample >= 32766.5f)
93995 out[i] = (ma_int16) 32767;
93996 else if (sample <= -32767.5f)
93997 out[i] = (ma_int16)-32768;
93998 else
93999 {
94000 short s = (ma_int16)(sample + .5f);
94001 s -= (s < 0);
94002 out[i] = s;
94003 }
94004 }
94005}
94006#ifndef MA_DR_MP3_SEEK_LEADING_MP3_FRAMES
94007#define MA_DR_MP3_SEEK_LEADING_MP3_FRAMES 2
94008#endif
94009#define MA_DR_MP3_MIN_DATA_CHUNK_SIZE 16384
94010#ifndef MA_DR_MP3_DATA_CHUNK_SIZE
94011#define MA_DR_MP3_DATA_CHUNK_SIZE (MA_DR_MP3_MIN_DATA_CHUNK_SIZE*4)
94012#endif
94013#define MA_DR_MP3_COUNTOF(x) (sizeof(x) / sizeof(x[0]))
94014#define MA_DR_MP3_CLAMP(x, lo, hi) (MA_DR_MP3_MAX(lo, MA_DR_MP3_MIN(x, hi)))
94015#ifndef MA_DR_MP3_PI_D
94016#define MA_DR_MP3_PI_D 3.14159265358979323846264
94017#endif
94018#define MA_DR_MP3_DEFAULT_RESAMPLER_LPF_ORDER 2
94019static MA_INLINE float ma_dr_mp3_mix_f32(float x, float y, float a)
94020{
94021 return x*(1-a) + y*a;
94022}
94023static MA_INLINE float ma_dr_mp3_mix_f32_fast(float x, float y, float a)
94024{
94025 float r0 = (y - x);
94026 float r1 = r0*a;
94027 return x + r1;
94028}
94029static MA_INLINE ma_uint32 ma_dr_mp3_gcf_u32(ma_uint32 a, ma_uint32 b)
94030{
94031 for (;;) {
94032 if (b == 0) {
94033 break;
94034 } else {
94035 ma_uint32 t = a;
94036 a = b;
94037 b = t % a;
94038 }
94039 }
94040 return a;
94041}
94042static void* ma_dr_mp3__malloc_default(size_t sz, void* pUserData)
94043{
94044 (void)pUserData;
94045 return MA_DR_MP3_MALLOC(sz);
94046}
94047static void* ma_dr_mp3__realloc_default(void* p, size_t sz, void* pUserData)
94048{
94049 (void)pUserData;
94050 return MA_DR_MP3_REALLOC(p, sz);
94051}
94052static void ma_dr_mp3__free_default(void* p, void* pUserData)
94053{
94054 (void)pUserData;
94055 MA_DR_MP3_FREE(p);
94056}
94057static void* ma_dr_mp3__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
94058{
94059 if (pAllocationCallbacks == NULL) {
94060 return NULL;
94061 }
94062 if (pAllocationCallbacks->onMalloc != NULL) {
94063 return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
94064 }
94065 if (pAllocationCallbacks->onRealloc != NULL) {
94066 return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
94067 }
94068 return NULL;
94069}
94070static void* ma_dr_mp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks)
94071{
94072 if (pAllocationCallbacks == NULL) {
94073 return NULL;
94074 }
94075 if (pAllocationCallbacks->onRealloc != NULL) {
94076 return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
94077 }
94078 if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
94079 void* p2;
94080 p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
94081 if (p2 == NULL) {
94082 return NULL;
94083 }
94084 if (p != NULL) {
94085 MA_DR_MP3_COPY_MEMORY(p2, p, szOld);
94086 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
94087 }
94088 return p2;
94089 }
94090 return NULL;
94091}
94092static void ma_dr_mp3__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
94093{
94094 if (p == NULL || pAllocationCallbacks == NULL) {
94095 return;
94096 }
94097 if (pAllocationCallbacks->onFree != NULL) {
94098 pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
94099 }
94100}
94101static ma_allocation_callbacks ma_dr_mp3_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks)
94102{
94103 if (pAllocationCallbacks != NULL) {
94104 return *pAllocationCallbacks;
94105 } else {
94106 ma_allocation_callbacks allocationCallbacks;
94107 allocationCallbacks.pUserData = NULL;
94108 allocationCallbacks.onMalloc = ma_dr_mp3__malloc_default;
94109 allocationCallbacks.onRealloc = ma_dr_mp3__realloc_default;
94110 allocationCallbacks.onFree = ma_dr_mp3__free_default;
94111 return allocationCallbacks;
94112 }
94113}
94114static size_t ma_dr_mp3__on_read(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead)
94115{
94116 size_t bytesRead;
94117 MA_DR_MP3_ASSERT(pMP3 != NULL);
94118 MA_DR_MP3_ASSERT(pMP3->onRead != NULL);
94119 if (bytesToRead == 0) {
94120 return 0;
94121 }
94122 bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead);
94123 pMP3->streamCursor += bytesRead;
94124 return bytesRead;
94125}
94126static size_t ma_dr_mp3__on_read_clamped(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead)
94127{
94128 MA_DR_MP3_ASSERT(pMP3 != NULL);
94129 MA_DR_MP3_ASSERT(pMP3->onRead != NULL);
94130 if (pMP3->streamLength == MA_UINT64_MAX) {
94131 return ma_dr_mp3__on_read(pMP3, pBufferOut, bytesToRead);
94132 } else {
94133 ma_uint64 bytesRemaining;
94134 bytesRemaining = (pMP3->streamLength - pMP3->streamCursor);
94135 if (bytesToRead > bytesRemaining) {
94136 bytesToRead = (size_t)bytesRemaining;
94137 }
94138 return ma_dr_mp3__on_read(pMP3, pBufferOut, bytesToRead);
94139 }
94140}
94141static ma_bool32 ma_dr_mp3__on_seek(ma_dr_mp3* pMP3, int offset, ma_dr_mp3_seek_origin origin)
94142{
94143 MA_DR_MP3_ASSERT(offset >= 0);
94144 MA_DR_MP3_ASSERT(origin == MA_DR_MP3_SEEK_SET || origin == MA_DR_MP3_SEEK_CUR);
94145 if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) {
94146 return MA_FALSE;
94147 }
94148 if (origin == MA_DR_MP3_SEEK_SET) {
94149 pMP3->streamCursor = (ma_uint64)offset;
94150 } else{
94151 pMP3->streamCursor += offset;
94152 }
94153 return MA_TRUE;
94154}
94155static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_mp3_seek_origin origin)
94156{
94157 if (offset <= 0x7FFFFFFF) {
94158 return ma_dr_mp3__on_seek(pMP3, (int)offset, origin);
94159 }
94160 if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, MA_DR_MP3_SEEK_SET)) {
94161 return MA_FALSE;
94162 }
94163 offset -= 0x7FFFFFFF;
94164 while (offset > 0) {
94165 if (offset <= 0x7FFFFFFF) {
94166 if (!ma_dr_mp3__on_seek(pMP3, (int)offset, MA_DR_MP3_SEEK_CUR)) {
94167 return MA_FALSE;
94168 }
94169 offset = 0;
94170 } else {
94171 if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, MA_DR_MP3_SEEK_CUR)) {
94172 return MA_FALSE;
94173 }
94174 offset -= 0x7FFFFFFF;
94175 }
94176 }
94177 return MA_TRUE;
94178}
94179static void ma_dr_mp3__on_meta(ma_dr_mp3* pMP3, ma_dr_mp3_metadata_type type, const void* pRawData, size_t rawDataSize)
94180{
94181 if (pMP3->onMeta) {
94182 ma_dr_mp3_metadata metadata;
94183 MA_DR_MP3_ZERO_OBJECT(&metadata);
94184 metadata.type = type;
94185 metadata.pRawData = pRawData;
94186 metadata.rawDataSize = rawDataSize;
94187 pMP3->onMeta(pMP3->pUserDataMeta, &metadata);
94188 }
94189}
94190static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames, ma_dr_mp3dec_frame_info* pMP3FrameInfo, const ma_uint8** ppMP3FrameData)
94191{
94192 ma_uint32 pcmFramesRead = 0;
94193 MA_DR_MP3_ASSERT(pMP3 != NULL);
94194 MA_DR_MP3_ASSERT(pMP3->onRead != NULL);
94195 if (pMP3->atEnd) {
94196 return 0;
94197 }
94198 for (;;) {
94199 ma_dr_mp3dec_frame_info info;
94200 if (pMP3->dataSize < MA_DR_MP3_MIN_DATA_CHUNK_SIZE) {
94201 size_t bytesRead;
94202 if (pMP3->pData != NULL) {
94203 MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
94204 }
94205 pMP3->dataConsumed = 0;
94206 if (pMP3->dataCapacity < MA_DR_MP3_DATA_CHUNK_SIZE) {
94207 ma_uint8* pNewData;
94208 size_t newDataCap;
94209 newDataCap = MA_DR_MP3_DATA_CHUNK_SIZE;
94210 pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
94211 if (pNewData == NULL) {
94212 return 0;
94213 }
94214 pMP3->pData = pNewData;
94215 pMP3->dataCapacity = newDataCap;
94216 }
94217 bytesRead = ma_dr_mp3__on_read_clamped(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
94218 if (bytesRead == 0) {
94219 if (pMP3->dataSize == 0) {
94220 pMP3->atEnd = MA_TRUE;
94221 return 0;
94222 }
94223 }
94224 pMP3->dataSize += bytesRead;
94225 }
94226 if (pMP3->dataSize > INT_MAX) {
94227 pMP3->atEnd = MA_TRUE;
94228 return 0;
94229 }
94230 MA_DR_MP3_ASSERT(pMP3->pData != NULL);
94231 MA_DR_MP3_ASSERT(pMP3->dataCapacity > 0);
94232 if (pMP3->pData == NULL) {
94233 return 0;
94234 }
94235 pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info);
94236 pMP3->dataConsumed += (size_t)info.frame_bytes;
94237 pMP3->dataSize -= (size_t)info.frame_bytes;
94238 if (pcmFramesRead > 0) {
94239 pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header);
94240 pMP3->pcmFramesConsumedInMP3Frame = 0;
94241 pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
94242 pMP3->mp3FrameChannels = info.channels;
94243 pMP3->mp3FrameSampleRate = info.sample_rate;
94244 if (pMP3FrameInfo != NULL) {
94245 *pMP3FrameInfo = info;
94246 }
94247 if (ppMP3FrameData != NULL) {
94248 *ppMP3FrameData = pMP3->pData + pMP3->dataConsumed - (size_t)info.frame_bytes;
94249 }
94250 break;
94251 } else if (info.frame_bytes == 0) {
94252 size_t bytesRead;
94253 MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
94254 pMP3->dataConsumed = 0;
94255 if (pMP3->dataCapacity == pMP3->dataSize) {
94256 ma_uint8* pNewData;
94257 size_t newDataCap;
94258 newDataCap = pMP3->dataCapacity + MA_DR_MP3_DATA_CHUNK_SIZE;
94259 pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
94260 if (pNewData == NULL) {
94261 return 0;
94262 }
94263 pMP3->pData = pNewData;
94264 pMP3->dataCapacity = newDataCap;
94265 }
94266 bytesRead = ma_dr_mp3__on_read_clamped(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
94267 if (bytesRead == 0) {
94268 pMP3->atEnd = MA_TRUE;
94269 return 0;
94270 }
94271 pMP3->dataSize += bytesRead;
94272 }
94273 };
94274 return pcmFramesRead;
94275}
94276static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames, ma_dr_mp3dec_frame_info* pMP3FrameInfo, const ma_uint8** ppMP3FrameData)
94277{
94278 ma_uint32 pcmFramesRead = 0;
94279 ma_dr_mp3dec_frame_info info;
94280 MA_DR_MP3_ASSERT(pMP3 != NULL);
94281 MA_DR_MP3_ASSERT(pMP3->memory.pData != NULL);
94282 if (pMP3->atEnd) {
94283 return 0;
94284 }
94285 for (;;) {
94286 pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info);
94287 if (pcmFramesRead > 0) {
94288 pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header);
94289 pMP3->pcmFramesConsumedInMP3Frame = 0;
94290 pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
94291 pMP3->mp3FrameChannels = info.channels;
94292 pMP3->mp3FrameSampleRate = info.sample_rate;
94293 if (pMP3FrameInfo != NULL) {
94294 *pMP3FrameInfo = info;
94295 }
94296 if (ppMP3FrameData != NULL) {
94297 *ppMP3FrameData = pMP3->memory.pData + pMP3->memory.currentReadPos;
94298 }
94299 break;
94300 } else if (info.frame_bytes > 0) {
94301 pMP3->memory.currentReadPos += (size_t)info.frame_bytes;
94302 pMP3->streamCursor += (size_t)info.frame_bytes;
94303 } else {
94304 break;
94305 }
94306 }
94307 pMP3->memory.currentReadPos += (size_t)info.frame_bytes;
94308 pMP3->streamCursor += (size_t)info.frame_bytes;
94309 return pcmFramesRead;
94310}
94311static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames, ma_dr_mp3dec_frame_info* pMP3FrameInfo, const ma_uint8** ppMP3FrameData)
94312{
94313 if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) {
94314 return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames, pMP3FrameInfo, ppMP3FrameData);
94315 } else {
94316 return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames, pMP3FrameInfo, ppMP3FrameData);
94317 }
94318}
94319static ma_uint32 ma_dr_mp3_decode_next_frame(ma_dr_mp3* pMP3)
94320{
94321 MA_DR_MP3_ASSERT(pMP3 != NULL);
94322 return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames, NULL, NULL);
94323}
94324#if 0
94325static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3)
94326{
94327 ma_uint32 pcmFrameCount;
94328 MA_DR_MP3_ASSERT(pMP3 != NULL);
94329 pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL);
94330 if (pcmFrameCount == 0) {
94331 return 0;
94332 }
94333 pMP3->currentPCMFrame += pcmFrameCount;
94334 pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount;
94335 pMP3->pcmFramesRemainingInMP3Frame = 0;
94336 return pcmFrameCount;
94337}
94338#endif
94339static ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, ma_dr_mp3_meta_proc onMeta, void* pUserData, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks)
94340{
94341 ma_dr_mp3dec_frame_info firstFrameInfo;
94342 const ma_uint8* pFirstFrameData;
94343 ma_uint32 firstFramePCMFrameCount;
94344 ma_uint32 detectedMP3FrameCount = 0xFFFFFFFF;
94345 MA_DR_MP3_ASSERT(pMP3 != NULL);
94346 MA_DR_MP3_ASSERT(onRead != NULL);
94347 ma_dr_mp3dec_init(&pMP3->decoder);
94348 pMP3->onRead = onRead;
94349 pMP3->onSeek = onSeek;
94350 pMP3->onMeta = onMeta;
94351 pMP3->pUserData = pUserData;
94352 pMP3->pUserDataMeta = pUserDataMeta;
94353 pMP3->allocationCallbacks = ma_dr_mp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
94354 if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) {
94355 return MA_FALSE;
94356 }
94357 pMP3->streamCursor = 0;
94358 pMP3->streamLength = MA_UINT64_MAX;
94359 pMP3->streamStartOffset = 0;
94360 pMP3->delayInPCMFrames = 0;
94361 pMP3->paddingInPCMFrames = 0;
94362 pMP3->totalPCMFrameCount = MA_UINT64_MAX;
94363 #if 1
94364 if (onSeek != NULL && onTell != NULL) {
94365 if (onSeek(pUserData, 0, MA_DR_MP3_SEEK_END)) {
94366 ma_int64 streamLen;
94367 int streamEndOffset = 0;
94368 if (onTell(pUserData, &streamLen)) {
94369 if (streamLen > 128) {
94370 char id3[3];
94371 if (onSeek(pUserData, streamEndOffset - 128, MA_DR_MP3_SEEK_END)) {
94372 if (onRead(pUserData, id3, 3) == 3 && id3[0] == 'T' && id3[1] == 'A' && id3[2] == 'G') {
94373 streamEndOffset -= 128;
94374 streamLen -= 128;
94375 if (onMeta != NULL) {
94376 ma_uint8 tag[128];
94377 tag[0] = 'T'; tag[1] = 'A'; tag[2] = 'G';
94378 if (onRead(pUserData, tag + 3, 125) == 125) {
94379 ma_dr_mp3__on_meta(pMP3, MA_DR_MP3_METADATA_TYPE_ID3V1, tag, 128);
94380 }
94381 }
94382 } else {
94383 }
94384 } else {
94385 }
94386 } else {
94387 }
94388 if (streamLen > 32) {
94389 char ape[32];
94390 if (onSeek(pUserData, streamEndOffset - 32, MA_DR_MP3_SEEK_END)) {
94391 if (onRead(pUserData, ape, 32) == 32 && ape[0] == 'A' && ape[1] == 'P' && ape[2] == 'E' && ape[3] == 'T' && ape[4] == 'A' && ape[5] == 'G' && ape[6] == 'E' && ape[7] == 'X') {
94392 ma_uint32 tagSize =
94393 ((ma_uint32)ape[24] << 0) |
94394 ((ma_uint32)ape[25] << 8) |
94395 ((ma_uint32)ape[26] << 16) |
94396 ((ma_uint32)ape[27] << 24);
94397 streamEndOffset -= 32 + tagSize;
94398 streamLen -= 32 + tagSize;
94399 if (onMeta != NULL) {
94400 if (onSeek(pUserData, streamEndOffset, MA_DR_MP3_SEEK_END)) {
94401 size_t apeTagSize = (size_t)tagSize + 32;
94402 ma_uint8* pTagData = (ma_uint8*)ma_dr_mp3_malloc(apeTagSize, pAllocationCallbacks);
94403 if (pTagData != NULL) {
94404 if (onRead(pUserData, pTagData, apeTagSize) == apeTagSize) {
94405 ma_dr_mp3__on_meta(pMP3, MA_DR_MP3_METADATA_TYPE_APE, pTagData, apeTagSize);
94406 }
94407 ma_dr_mp3_free(pTagData, pAllocationCallbacks);
94408 }
94409 }
94410 }
94411 }
94412 }
94413 } else {
94414 }
94415 if (!onSeek(pUserData, 0, MA_DR_MP3_SEEK_SET)) {
94416 return MA_FALSE;
94417 }
94418 pMP3->streamLength = (ma_uint64)streamLen;
94419 if (pMP3->memory.pData != NULL) {
94420 pMP3->memory.dataSize = (size_t)pMP3->streamLength;
94421 }
94422 } else {
94423 if (!onSeek(pUserData, 0, MA_DR_MP3_SEEK_SET)) {
94424 return MA_FALSE;
94425 }
94426 }
94427 } else {
94428 }
94429 } else {
94430 }
94431 #endif
94432 #if 1
94433 {
94434 char header[10];
94435 if (onRead(pUserData, header, 10) == 10) {
94436 if (header[0] == 'I' && header[1] == 'D' && header[2] == '3') {
94437 ma_uint32 tagSize =
94438 (((ma_uint32)header[6] & 0x7F) << 21) |
94439 (((ma_uint32)header[7] & 0x7F) << 14) |
94440 (((ma_uint32)header[8] & 0x7F) << 7) |
94441 (((ma_uint32)header[9] & 0x7F) << 0);
94442 if (header[5] & 0x10) {
94443 tagSize += 10;
94444 }
94445 if (onMeta != NULL) {
94446 size_t tagSizeWithHeader = 10 + tagSize;
94447 ma_uint8* pTagData = (ma_uint8*)ma_dr_mp3_malloc(tagSizeWithHeader, pAllocationCallbacks);
94448 if (pTagData != NULL) {
94449 MA_DR_MP3_COPY_MEMORY(pTagData, header, 10);
94450 if (onRead(pUserData, pTagData + 10, tagSize) == tagSize) {
94451 ma_dr_mp3__on_meta(pMP3, MA_DR_MP3_METADATA_TYPE_ID3V2, pTagData, tagSizeWithHeader);
94452 }
94453 ma_dr_mp3_free(pTagData, pAllocationCallbacks);
94454 }
94455 } else {
94456 if (onSeek != NULL) {
94457 if (!onSeek(pUserData, tagSize, MA_DR_MP3_SEEK_CUR)) {
94458 return MA_FALSE;
94459 }
94460 } else {
94461 char discard[1024];
94462 while (tagSize > 0) {
94463 size_t bytesToRead = tagSize;
94464 if (bytesToRead > sizeof(discard)) {
94465 bytesToRead = sizeof(discard);
94466 }
94467 if (onRead(pUserData, discard, bytesToRead) != bytesToRead) {
94468 return MA_FALSE;
94469 }
94470 tagSize -= (ma_uint32)bytesToRead;
94471 }
94472 }
94473 }
94474 pMP3->streamStartOffset += 10 + tagSize;
94475 pMP3->streamCursor = pMP3->streamStartOffset;
94476 } else {
94477 if (onSeek != NULL) {
94478 if (!onSeek(pUserData, 0, MA_DR_MP3_SEEK_SET)) {
94479 return MA_FALSE;
94480 }
94481 } else {
94482 }
94483 }
94484 } else {
94485 return MA_FALSE;
94486 }
94487 }
94488 #endif
94489 firstFramePCMFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames, &firstFrameInfo, &pFirstFrameData);
94490 if (firstFramePCMFrameCount > 0) {
94491 MA_DR_MP3_ASSERT(pFirstFrameData != NULL);
94492 #if 1
94493 MA_DR_MP3_ASSERT(firstFrameInfo.frame_bytes > 0);
94494 {
94495 ma_dr_mp3_bs bs;
94496 ma_dr_mp3_L3_gr_info grInfo[4];
94497 const ma_uint8* pTagData = pFirstFrameData;
94498 ma_dr_mp3_bs_init(&bs, pFirstFrameData + MA_DR_MP3_HDR_SIZE, firstFrameInfo.frame_bytes - MA_DR_MP3_HDR_SIZE);
94499 if (MA_DR_MP3_HDR_IS_CRC(pFirstFrameData)) {
94500 ma_dr_mp3_bs_get_bits(&bs, 16);
94501 }
94502 if (ma_dr_mp3_L3_read_side_info(&bs, grInfo, pFirstFrameData) >= 0) {
94503 ma_bool32 isXing = MA_FALSE;
94504 ma_bool32 isInfo = MA_FALSE;
94505 const ma_uint8* pTagDataBeg;
94506 pTagDataBeg = pFirstFrameData + MA_DR_MP3_HDR_SIZE + (bs.pos/8);
94507 pTagData = pTagDataBeg;
94508 isXing = (pTagData[0] == 'X' && pTagData[1] == 'i' && pTagData[2] == 'n' && pTagData[3] == 'g');
94509 isInfo = (pTagData[0] == 'I' && pTagData[1] == 'n' && pTagData[2] == 'f' && pTagData[3] == 'o');
94510 if (isXing || isInfo) {
94511 ma_uint32 bytes = 0;
94512 ma_uint32 flags = pTagData[7];
94513 pTagData += 8;
94514 if (flags & 0x01) {
94515 detectedMP3FrameCount = (ma_uint32)pTagData[0] << 24 | (ma_uint32)pTagData[1] << 16 | (ma_uint32)pTagData[2] << 8 | (ma_uint32)pTagData[3];
94516 pTagData += 4;
94517 }
94518 if (flags & 0x02) {
94519 bytes = (ma_uint32)pTagData[0] << 24 | (ma_uint32)pTagData[1] << 16 | (ma_uint32)pTagData[2] << 8 | (ma_uint32)pTagData[3];
94520 (void)bytes;
94521 pTagData += 4;
94522 }
94523 if (flags & 0x04) {
94524 pTagData += 100;
94525 }
94526 if (flags & 0x08) {
94527 pTagData += 4;
94528 }
94529 if (pTagData[0]) {
94530 pTagData += 21;
94531 if (pTagData - pFirstFrameData + 14 < firstFrameInfo.frame_bytes) {
94532 int delayInPCMFrames;
94533 int paddingInPCMFrames;
94534 delayInPCMFrames = (( (ma_uint32)pTagData[0] << 4) | ((ma_uint32)pTagData[1] >> 4)) + (528 + 1);
94535 paddingInPCMFrames = ((((ma_uint32)pTagData[1] & 0xF) << 8) | ((ma_uint32)pTagData[2] )) - (528 + 1);
94536 if (paddingInPCMFrames < 0) {
94537 paddingInPCMFrames = 0;
94538 }
94539 pMP3->delayInPCMFrames = (ma_uint32)delayInPCMFrames;
94540 pMP3->paddingInPCMFrames = (ma_uint32)paddingInPCMFrames;
94541 }
94542 }
94543 if (isXing) {
94544 pMP3->isVBR = MA_TRUE;
94545 } else if (isInfo) {
94546 pMP3->isCBR = MA_TRUE;
94547 }
94548 if (onMeta != NULL) {
94549 ma_dr_mp3_metadata_type metadataType = isXing ? MA_DR_MP3_METADATA_TYPE_XING : MA_DR_MP3_METADATA_TYPE_VBRI;
94550 size_t tagDataSize;
94551 tagDataSize = (size_t)firstFrameInfo.frame_bytes;
94552 tagDataSize -= (size_t)(pTagDataBeg - pFirstFrameData);
94553 ma_dr_mp3__on_meta(pMP3, metadataType, pTagDataBeg, tagDataSize);
94554 }
94555 pMP3->pcmFramesRemainingInMP3Frame = 0;
94556 pMP3->streamStartOffset += (ma_uint32)(firstFrameInfo.frame_bytes);
94557 pMP3->streamCursor = pMP3->streamStartOffset;
94558 }
94559 } else {
94560 }
94561 }
94562 #endif
94563 } else {
94564 ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
94565 return MA_FALSE;
94566 }
94567 if (detectedMP3FrameCount != 0xFFFFFFFF) {
94568 pMP3->totalPCMFrameCount = detectedMP3FrameCount * firstFramePCMFrameCount;
94569 }
94570 pMP3->channels = pMP3->mp3FrameChannels;
94571 pMP3->sampleRate = pMP3->mp3FrameSampleRate;
94572 return MA_TRUE;
94573}
94574MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, ma_dr_mp3_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
94575{
94576 if (pMP3 == NULL || onRead == NULL) {
94577 return MA_FALSE;
94578 }
94579 MA_DR_MP3_ZERO_OBJECT(pMP3);
94580 return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, onTell, onMeta, pUserData, pUserData, pAllocationCallbacks);
94581}
94582static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
94583{
94584 ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData;
94585 size_t bytesRemaining;
94586 MA_DR_MP3_ASSERT(pMP3 != NULL);
94587 MA_DR_MP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos);
94588 bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos;
94589 if (bytesToRead > bytesRemaining) {
94590 bytesToRead = bytesRemaining;
94591 }
94592 if (bytesToRead > 0) {
94593 MA_DR_MP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead);
94594 pMP3->memory.currentReadPos += bytesToRead;
94595 }
94596 return bytesToRead;
94597}
94598static ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_dr_mp3_seek_origin origin)
94599{
94600 ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData;
94601 ma_int64 newCursor;
94602 MA_DR_MP3_ASSERT(pMP3 != NULL);
94603 newCursor = pMP3->memory.currentReadPos;
94604 if (origin == MA_DR_MP3_SEEK_SET) {
94605 newCursor = 0;
94606 } else if (origin == MA_DR_MP3_SEEK_CUR) {
94607 newCursor = (ma_int64)pMP3->memory.currentReadPos;
94608 } else if (origin == MA_DR_MP3_SEEK_END) {
94609 newCursor = (ma_int64)pMP3->memory.dataSize;
94610 } else {
94611 MA_DR_MP3_ASSERT(!"Invalid seek origin");
94612 return MA_FALSE;
94613 }
94614 newCursor += byteOffset;
94615 if (newCursor < 0) {
94616 return MA_FALSE;
94617 }
94618 if ((size_t)newCursor > pMP3->memory.dataSize) {
94619 return MA_FALSE;
94620 }
94621 pMP3->memory.currentReadPos = (size_t)newCursor;
94622 return MA_TRUE;
94623}
94624static ma_bool32 ma_dr_mp3__on_tell_memory(void* pUserData, ma_int64* pCursor)
94625{
94626 ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData;
94627 MA_DR_MP3_ASSERT(pMP3 != NULL);
94628 MA_DR_MP3_ASSERT(pCursor != NULL);
94629 *pCursor = (ma_int64)pMP3->memory.currentReadPos;
94630 return MA_TRUE;
94631}
94632MA_API ma_bool32 ma_dr_mp3_init_memory_with_metadata(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks)
94633{
94634 ma_bool32 result;
94635 if (pMP3 == NULL) {
94636 return MA_FALSE;
94637 }
94638 MA_DR_MP3_ZERO_OBJECT(pMP3);
94639 if (pData == NULL || dataSize == 0) {
94640 return MA_FALSE;
94641 }
94642 pMP3->memory.pData = (const ma_uint8*)pData;
94643 pMP3->memory.dataSize = dataSize;
94644 pMP3->memory.currentReadPos = 0;
94645 result = ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, ma_dr_mp3__on_tell_memory, onMeta, pMP3, pUserDataMeta, pAllocationCallbacks);
94646 if (result == MA_FALSE) {
94647 return MA_FALSE;
94648 }
94649 if (pMP3->streamLength <= (ma_uint64)MA_SIZE_MAX) {
94650 pMP3->memory.dataSize = (size_t)pMP3->streamLength;
94651 }
94652 if (pMP3->streamStartOffset > (ma_uint64)MA_SIZE_MAX) {
94653 return MA_FALSE;
94654 }
94655 return MA_TRUE;
94656}
94657MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks)
94658{
94659 return ma_dr_mp3_init_memory_with_metadata(pMP3, pData, dataSize, NULL, NULL, pAllocationCallbacks);
94660}
94661#ifndef MA_DR_MP3_NO_STDIO
94662#include <stdio.h>
94663#include <wchar.h>
94664static size_t ma_dr_mp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
94665{
94666 return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
94667}
94668static ma_bool32 ma_dr_mp3__on_seek_stdio(void* pUserData, int offset, ma_dr_mp3_seek_origin origin)
94669{
94670 int whence = SEEK_SET;
94671 if (origin == MA_DR_MP3_SEEK_CUR) {
94672 whence = SEEK_CUR;
94673 } else if (origin == MA_DR_MP3_SEEK_END) {
94674 whence = SEEK_END;
94675 }
94676 return fseek((FILE*)pUserData, offset, whence) == 0;
94677}
94678static ma_bool32 ma_dr_mp3__on_tell_stdio(void* pUserData, ma_int64* pCursor)
94679{
94680 FILE* pFileStdio = (FILE*)pUserData;
94681 ma_int64 result;
94682 MA_DR_MP3_ASSERT(pFileStdio != NULL);
94683 MA_DR_MP3_ASSERT(pCursor != NULL);
94684#if defined(_WIN32)
94685 #if defined(_MSC_VER) && _MSC_VER > 1200
94686 result = _ftelli64(pFileStdio);
94687 #else
94688 result = ftell(pFileStdio);
94689 #endif
94690#else
94691 result = ftell(pFileStdio);
94692#endif
94693 *pCursor = result;
94694 return MA_TRUE;
94695}
94696MA_API ma_bool32 ma_dr_mp3_init_file_with_metadata(ma_dr_mp3* pMP3, const char* pFilePath, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks)
94697{
94698 ma_bool32 result;
94699 FILE* pFile;
94700 if (pMP3 == NULL) {
94701 return MA_FALSE;
94702 }
94703 MA_DR_MP3_ZERO_OBJECT(pMP3);
94704 if (ma_fopen(&pFile, pFilePath, "rb") != MA_SUCCESS) {
94705 return MA_FALSE;
94706 }
94707 result = ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, ma_dr_mp3__on_tell_stdio, onMeta, (void*)pFile, pUserDataMeta, pAllocationCallbacks);
94708 if (result != MA_TRUE) {
94709 fclose(pFile);
94710 return result;
94711 }
94712 return MA_TRUE;
94713}
94714MA_API ma_bool32 ma_dr_mp3_init_file_with_metadata_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks)
94715{
94716 ma_bool32 result;
94717 FILE* pFile;
94718 if (pMP3 == NULL) {
94719 return MA_FALSE;
94720 }
94721 MA_DR_MP3_ZERO_OBJECT(pMP3);
94722 if (ma_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != MA_SUCCESS) {
94723 return MA_FALSE;
94724 }
94725 result = ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, ma_dr_mp3__on_tell_stdio, onMeta, (void*)pFile, pUserDataMeta, pAllocationCallbacks);
94726 if (result != MA_TRUE) {
94727 fclose(pFile);
94728 return result;
94729 }
94730 return MA_TRUE;
94731}
94732MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks)
94733{
94734 return ma_dr_mp3_init_file_with_metadata(pMP3, pFilePath, NULL, NULL, pAllocationCallbacks);
94735}
94736MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks)
94737{
94738 return ma_dr_mp3_init_file_with_metadata_w(pMP3, pFilePath, NULL, NULL, pAllocationCallbacks);
94739}
94740#endif
94741MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3)
94742{
94743 if (pMP3 == NULL) {
94744 return;
94745 }
94746#ifndef MA_DR_MP3_NO_STDIO
94747 if (pMP3->onRead == ma_dr_mp3__on_read_stdio) {
94748 FILE* pFile = (FILE*)pMP3->pUserData;
94749 if (pFile != NULL) {
94750 fclose(pFile);
94751 pMP3->pUserData = NULL;
94752 }
94753 }
94754#endif
94755 ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
94756}
94757#if defined(MA_DR_MP3_FLOAT_OUTPUT)
94758static void ma_dr_mp3_f32_to_s16(ma_int16* dst, const float* src, ma_uint64 sampleCount)
94759{
94760 ma_uint64 i;
94761 ma_uint64 i4;
94762 ma_uint64 sampleCount4;
94763 i = 0;
94764 sampleCount4 = sampleCount >> 2;
94765 for (i4 = 0; i4 < sampleCount4; i4 += 1) {
94766 float x0 = src[i+0];
94767 float x1 = src[i+1];
94768 float x2 = src[i+2];
94769 float x3 = src[i+3];
94770 x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
94771 x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
94772 x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
94773 x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
94774 x0 = x0 * 32767.0f;
94775 x1 = x1 * 32767.0f;
94776 x2 = x2 * 32767.0f;
94777 x3 = x3 * 32767.0f;
94778 dst[i+0] = (ma_int16)x0;
94779 dst[i+1] = (ma_int16)x1;
94780 dst[i+2] = (ma_int16)x2;
94781 dst[i+3] = (ma_int16)x3;
94782 i += 4;
94783 }
94784 for (; i < sampleCount; i += 1) {
94785 float x = src[i];
94786 x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
94787 x = x * 32767.0f;
94788 dst[i] = (ma_int16)x;
94789 }
94790}
94791#endif
94792#if !defined(MA_DR_MP3_FLOAT_OUTPUT)
94793static void ma_dr_mp3_s16_to_f32(float* dst, const ma_int16* src, ma_uint64 sampleCount)
94794{
94795 ma_uint64 i;
94796 for (i = 0; i < sampleCount; i += 1) {
94797 float x = (float)src[i];
94798 x = x * 0.000030517578125f;
94799 dst[i] = x;
94800 }
94801}
94802#endif
94803static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 framesToRead, void* pBufferOut)
94804{
94805 ma_uint64 totalFramesRead = 0;
94806 MA_DR_MP3_ASSERT(pMP3 != NULL);
94807 MA_DR_MP3_ASSERT(pMP3->onRead != NULL);
94808 while (framesToRead > 0) {
94809 ma_uint32 framesToConsume;
94810 if (pMP3->currentPCMFrame < pMP3->delayInPCMFrames) {
94811 ma_uint32 framesToSkip = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, pMP3->delayInPCMFrames - pMP3->currentPCMFrame);
94812 pMP3->currentPCMFrame += framesToSkip;
94813 pMP3->pcmFramesConsumedInMP3Frame += framesToSkip;
94814 pMP3->pcmFramesRemainingInMP3Frame -= framesToSkip;
94815 }
94816 framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead);
94817 if (pMP3->totalPCMFrameCount != MA_UINT64_MAX && pMP3->totalPCMFrameCount > pMP3->paddingInPCMFrames) {
94818 if (pMP3->currentPCMFrame < (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames)) {
94819 ma_uint64 framesRemainigToPadding = (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames) - pMP3->currentPCMFrame;
94820 if (framesToConsume > framesRemainigToPadding) {
94821 framesToConsume = (ma_uint32)framesRemainigToPadding;
94822 }
94823 } else {
94824 break;
94825 }
94826 }
94827 if (pBufferOut != NULL) {
94828 #if defined(MA_DR_MP3_FLOAT_OUTPUT)
94829 {
94830 float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels);
94831 float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
94832 MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels);
94833 }
94834 #else
94835 {
94836 ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels);
94837 ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
94838 MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels);
94839 }
94840 #endif
94841 }
94842 pMP3->currentPCMFrame += framesToConsume;
94843 pMP3->pcmFramesConsumedInMP3Frame += framesToConsume;
94844 pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume;
94845 totalFramesRead += framesToConsume;
94846 framesToRead -= framesToConsume;
94847 if (framesToRead == 0) {
94848 break;
94849 }
94850 if (pMP3->totalPCMFrameCount != MA_UINT64_MAX && pMP3->totalPCMFrameCount > pMP3->paddingInPCMFrames && pMP3->currentPCMFrame >= (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames)) {
94851 break;
94852 }
94853 MA_DR_MP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0);
94854 if (ma_dr_mp3_decode_next_frame(pMP3) == 0) {
94855 break;
94856 }
94857 }
94858 return totalFramesRead;
94859}
94860MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut)
94861{
94862 if (pMP3 == NULL || pMP3->onRead == NULL) {
94863 return 0;
94864 }
94865#if defined(MA_DR_MP3_FLOAT_OUTPUT)
94866 return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
94867#else
94868 {
94869 ma_int16 pTempS16[8192];
94870 ma_uint64 totalPCMFramesRead = 0;
94871 while (totalPCMFramesRead < framesToRead) {
94872 ma_uint64 framesJustRead;
94873 ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
94874 ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempS16) / pMP3->channels;
94875 if (framesToReadNow > framesRemaining) {
94876 framesToReadNow = framesRemaining;
94877 }
94878 framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16);
94879 if (framesJustRead == 0) {
94880 break;
94881 }
94882 ma_dr_mp3_s16_to_f32((float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels);
94883 totalPCMFramesRead += framesJustRead;
94884 }
94885 return totalPCMFramesRead;
94886 }
94887#endif
94888}
94889MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut)
94890{
94891 if (pMP3 == NULL || pMP3->onRead == NULL) {
94892 return 0;
94893 }
94894#if !defined(MA_DR_MP3_FLOAT_OUTPUT)
94895 return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
94896#else
94897 {
94898 float pTempF32[4096];
94899 ma_uint64 totalPCMFramesRead = 0;
94900 while (totalPCMFramesRead < framesToRead) {
94901 ma_uint64 framesJustRead;
94902 ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
94903 ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempF32) / pMP3->channels;
94904 if (framesToReadNow > framesRemaining) {
94905 framesToReadNow = framesRemaining;
94906 }
94907 framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32);
94908 if (framesJustRead == 0) {
94909 break;
94910 }
94911 ma_dr_mp3_f32_to_s16((ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels);
94912 totalPCMFramesRead += framesJustRead;
94913 }
94914 return totalPCMFramesRead;
94915 }
94916#endif
94917}
94918static void ma_dr_mp3_reset(ma_dr_mp3* pMP3)
94919{
94920 MA_DR_MP3_ASSERT(pMP3 != NULL);
94921 pMP3->pcmFramesConsumedInMP3Frame = 0;
94922 pMP3->pcmFramesRemainingInMP3Frame = 0;
94923 pMP3->currentPCMFrame = 0;
94924 pMP3->dataSize = 0;
94925 pMP3->atEnd = MA_FALSE;
94926 ma_dr_mp3dec_init(&pMP3->decoder);
94927}
94928static ma_bool32 ma_dr_mp3_seek_to_start_of_stream(ma_dr_mp3* pMP3)
94929{
94930 MA_DR_MP3_ASSERT(pMP3 != NULL);
94931 MA_DR_MP3_ASSERT(pMP3->onSeek != NULL);
94932 if (!ma_dr_mp3__on_seek_64(pMP3, pMP3->streamStartOffset, MA_DR_MP3_SEEK_SET)) {
94933 return MA_FALSE;
94934 }
94935 ma_dr_mp3_reset(pMP3);
94936 return MA_TRUE;
94937}
94938static ma_bool32 ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameOffset)
94939{
94940 ma_uint64 framesRead;
94941#if defined(MA_DR_MP3_FLOAT_OUTPUT)
94942 framesRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, frameOffset, NULL);
94943#else
94944 framesRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, frameOffset, NULL);
94945#endif
94946 if (framesRead != frameOffset) {
94947 return MA_FALSE;
94948 }
94949 return MA_TRUE;
94950}
94951static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameIndex)
94952{
94953 MA_DR_MP3_ASSERT(pMP3 != NULL);
94954 if (frameIndex == pMP3->currentPCMFrame) {
94955 return MA_TRUE;
94956 }
94957 if (frameIndex < pMP3->currentPCMFrame) {
94958 if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {
94959 return MA_FALSE;
94960 }
94961 }
94962 MA_DR_MP3_ASSERT(frameIndex >= pMP3->currentPCMFrame);
94963 return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame));
94964}
94965static ma_bool32 ma_dr_mp3_find_closest_seek_point(ma_dr_mp3* pMP3, ma_uint64 frameIndex, ma_uint32* pSeekPointIndex)
94966{
94967 ma_uint32 iSeekPoint;
94968 MA_DR_MP3_ASSERT(pSeekPointIndex != NULL);
94969 *pSeekPointIndex = 0;
94970 if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) {
94971 return MA_FALSE;
94972 }
94973 for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) {
94974 if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) {
94975 break;
94976 }
94977 *pSeekPointIndex = iSeekPoint;
94978 }
94979 return MA_TRUE;
94980}
94981static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uint64 frameIndex)
94982{
94983 ma_dr_mp3_seek_point seekPoint;
94984 ma_uint32 priorSeekPointIndex;
94985 ma_uint16 iMP3Frame;
94986 ma_uint64 leftoverFrames;
94987 MA_DR_MP3_ASSERT(pMP3 != NULL);
94988 MA_DR_MP3_ASSERT(pMP3->pSeekPoints != NULL);
94989 MA_DR_MP3_ASSERT(pMP3->seekPointCount > 0);
94990 if (ma_dr_mp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) {
94991 seekPoint = pMP3->pSeekPoints[priorSeekPointIndex];
94992 } else {
94993 seekPoint.seekPosInBytes = 0;
94994 seekPoint.pcmFrameIndex = 0;
94995 seekPoint.mp3FramesToDiscard = 0;
94996 seekPoint.pcmFramesToDiscard = 0;
94997 }
94998 if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, MA_DR_MP3_SEEK_SET)) {
94999 return MA_FALSE;
95000 }
95001 ma_dr_mp3_reset(pMP3);
95002 for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) {
95003 ma_uint32 pcmFramesRead;
95004 ma_dr_mp3d_sample_t* pPCMFrames;
95005 pPCMFrames = NULL;
95006 if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) {
95007 pPCMFrames = (ma_dr_mp3d_sample_t*)pMP3->pcmFrames;
95008 }
95009 pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames, NULL, NULL);
95010 if (pcmFramesRead == 0) {
95011 return MA_FALSE;
95012 }
95013 }
95014 pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard;
95015 leftoverFrames = frameIndex - pMP3->currentPCMFrame;
95016 return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames);
95017}
95018MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex)
95019{
95020 if (pMP3 == NULL || pMP3->onSeek == NULL) {
95021 return MA_FALSE;
95022 }
95023 if (frameIndex == 0) {
95024 return ma_dr_mp3_seek_to_start_of_stream(pMP3);
95025 }
95026 if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) {
95027 return ma_dr_mp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex);
95028 } else {
95029 return ma_dr_mp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex);
95030 }
95031}
95032MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount)
95033{
95034 ma_uint64 currentPCMFrame;
95035 ma_uint64 totalPCMFrameCount;
95036 ma_uint64 totalMP3FrameCount;
95037 if (pMP3 == NULL) {
95038 return MA_FALSE;
95039 }
95040 if (pMP3->onSeek == NULL) {
95041 return MA_FALSE;
95042 }
95043 currentPCMFrame = pMP3->currentPCMFrame;
95044 if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {
95045 return MA_FALSE;
95046 }
95047 totalPCMFrameCount = 0;
95048 totalMP3FrameCount = 0;
95049 for (;;) {
95050 ma_uint32 pcmFramesInCurrentMP3Frame;
95051 pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL);
95052 if (pcmFramesInCurrentMP3Frame == 0) {
95053 break;
95054 }
95055 totalPCMFrameCount += pcmFramesInCurrentMP3Frame;
95056 totalMP3FrameCount += 1;
95057 }
95058 if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {
95059 return MA_FALSE;
95060 }
95061 if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
95062 return MA_FALSE;
95063 }
95064 if (pMP3FrameCount != NULL) {
95065 *pMP3FrameCount = totalMP3FrameCount;
95066 }
95067 if (pPCMFrameCount != NULL) {
95068 *pPCMFrameCount = totalPCMFrameCount;
95069 }
95070 return MA_TRUE;
95071}
95072MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3)
95073{
95074 ma_uint64 totalPCMFrameCount;
95075 if (pMP3 == NULL) {
95076 return 0;
95077 }
95078 if (pMP3->totalPCMFrameCount != MA_UINT64_MAX) {
95079 totalPCMFrameCount = pMP3->totalPCMFrameCount;
95080 if (totalPCMFrameCount >= pMP3->delayInPCMFrames) {
95081 totalPCMFrameCount -= pMP3->delayInPCMFrames;
95082 } else {
95083 }
95084 if (totalPCMFrameCount >= pMP3->paddingInPCMFrames) {
95085 totalPCMFrameCount -= pMP3->paddingInPCMFrames;
95086 } else {
95087 }
95088 return totalPCMFrameCount;
95089 } else {
95090 if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) {
95091 return 0;
95092 }
95093 return totalPCMFrameCount;
95094 }
95095}
95096MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3)
95097{
95098 ma_uint64 totalMP3FrameCount;
95099 if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) {
95100 return 0;
95101 }
95102 return totalMP3FrameCount;
95103}
95104static void ma_dr_mp3__accumulate_running_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint32 pcmFrameCountIn, ma_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart)
95105{
95106 float srcRatio;
95107 float pcmFrameCountOutF;
95108 ma_uint32 pcmFrameCountOut;
95109 srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate;
95110 MA_DR_MP3_ASSERT(srcRatio > 0);
95111 pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio);
95112 pcmFrameCountOut = (ma_uint32)pcmFrameCountOutF;
95113 *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut;
95114 *pRunningPCMFrameCount += pcmFrameCountOut;
95115}
95116typedef struct
95117{
95118 ma_uint64 bytePos;
95119 ma_uint64 pcmFrameIndex;
95120} ma_dr_mp3__seeking_mp3_frame_info;
95121MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints)
95122{
95123 ma_uint32 seekPointCount;
95124 ma_uint64 currentPCMFrame;
95125 ma_uint64 totalMP3FrameCount;
95126 ma_uint64 totalPCMFrameCount;
95127 if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) {
95128 return MA_FALSE;
95129 }
95130 seekPointCount = *pSeekPointCount;
95131 if (seekPointCount == 0) {
95132 return MA_FALSE;
95133 }
95134 currentPCMFrame = pMP3->currentPCMFrame;
95135 if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) {
95136 return MA_FALSE;
95137 }
95138 if (totalMP3FrameCount < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1) {
95139 seekPointCount = 1;
95140 pSeekPoints[0].seekPosInBytes = 0;
95141 pSeekPoints[0].pcmFrameIndex = 0;
95142 pSeekPoints[0].mp3FramesToDiscard = 0;
95143 pSeekPoints[0].pcmFramesToDiscard = 0;
95144 } else {
95145 ma_uint64 pcmFramesBetweenSeekPoints;
95146 ma_dr_mp3__seeking_mp3_frame_info mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1];
95147 ma_uint64 runningPCMFrameCount = 0;
95148 float runningPCMFrameCountFractionalPart = 0;
95149 ma_uint64 nextTargetPCMFrame;
95150 ma_uint32 iMP3Frame;
95151 ma_uint32 iSeekPoint;
95152 if (seekPointCount > totalMP3FrameCount-1) {
95153 seekPointCount = (ma_uint32)totalMP3FrameCount-1;
95154 }
95155 pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1);
95156 if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {
95157 return MA_FALSE;
95158 }
95159 for (iMP3Frame = 0; iMP3Frame < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) {
95160 ma_uint32 pcmFramesInCurrentMP3FrameIn;
95161 MA_DR_MP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize);
95162 mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize;
95163 mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount;
95164 pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL);
95165 if (pcmFramesInCurrentMP3FrameIn == 0) {
95166 return MA_FALSE;
95167 }
95168 ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
95169 }
95170 nextTargetPCMFrame = 0;
95171 for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) {
95172 nextTargetPCMFrame += pcmFramesBetweenSeekPoints;
95173 for (;;) {
95174 if (nextTargetPCMFrame < runningPCMFrameCount) {
95175 pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
95176 pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
95177 pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES;
95178 pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
95179 break;
95180 } else {
95181 size_t i;
95182 ma_uint32 pcmFramesInCurrentMP3FrameIn;
95183 for (i = 0; i < MA_DR_MP3_COUNTOF(mp3FrameInfo)-1; ++i) {
95184 mp3FrameInfo[i] = mp3FrameInfo[i+1];
95185 }
95186 mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize;
95187 mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount;
95188 pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL);
95189 if (pcmFramesInCurrentMP3FrameIn == 0) {
95190 pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
95191 pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
95192 pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES;
95193 pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
95194 break;
95195 }
95196 ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
95197 }
95198 }
95199 }
95200 if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {
95201 return MA_FALSE;
95202 }
95203 if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
95204 return MA_FALSE;
95205 }
95206 }
95207 *pSeekPointCount = seekPointCount;
95208 return MA_TRUE;
95209}
95210MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints)
95211{
95212 if (pMP3 == NULL) {
95213 return MA_FALSE;
95214 }
95215 if (seekPointCount == 0 || pSeekPoints == NULL) {
95216 pMP3->seekPointCount = 0;
95217 pMP3->pSeekPoints = NULL;
95218 } else {
95219 pMP3->seekPointCount = seekPointCount;
95220 pMP3->pSeekPoints = pSeekPoints;
95221 }
95222 return MA_TRUE;
95223}
95224static float* ma_dr_mp3__full_read_and_close_f32(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount)
95225{
95226 ma_uint64 totalFramesRead = 0;
95227 ma_uint64 framesCapacity = 0;
95228 float* pFrames = NULL;
95229 float temp[4096];
95230 MA_DR_MP3_ASSERT(pMP3 != NULL);
95231 for (;;) {
95232 ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels;
95233 ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);
95234 if (framesJustRead == 0) {
95235 break;
95236 }
95237 if (framesCapacity < totalFramesRead + framesJustRead) {
95238 ma_uint64 oldFramesBufferSize;
95239 ma_uint64 newFramesBufferSize;
95240 ma_uint64 newFramesCap;
95241 float* pNewFrames;
95242 newFramesCap = framesCapacity * 2;
95243 if (newFramesCap < totalFramesRead + framesJustRead) {
95244 newFramesCap = totalFramesRead + framesJustRead;
95245 }
95246 oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float);
95247 newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float);
95248 if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) {
95249 break;
95250 }
95251 pNewFrames = (float*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
95252 if (pNewFrames == NULL) {
95253 ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
95254 break;
95255 }
95256 pFrames = pNewFrames;
95257 framesCapacity = newFramesCap;
95258 }
95259 MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float)));
95260 totalFramesRead += framesJustRead;
95261 if (framesJustRead != framesToReadRightNow) {
95262 break;
95263 }
95264 }
95265 if (pConfig != NULL) {
95266 pConfig->channels = pMP3->channels;
95267 pConfig->sampleRate = pMP3->sampleRate;
95268 }
95269 ma_dr_mp3_uninit(pMP3);
95270 if (pTotalFrameCount) {
95271 *pTotalFrameCount = totalFramesRead;
95272 }
95273 return pFrames;
95274}
95275static ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount)
95276{
95277 ma_uint64 totalFramesRead = 0;
95278 ma_uint64 framesCapacity = 0;
95279 ma_int16* pFrames = NULL;
95280 ma_int16 temp[4096];
95281 MA_DR_MP3_ASSERT(pMP3 != NULL);
95282 for (;;) {
95283 ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels;
95284 ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp);
95285 if (framesJustRead == 0) {
95286 break;
95287 }
95288 if (framesCapacity < totalFramesRead + framesJustRead) {
95289 ma_uint64 newFramesBufferSize;
95290 ma_uint64 oldFramesBufferSize;
95291 ma_uint64 newFramesCap;
95292 ma_int16* pNewFrames;
95293 newFramesCap = framesCapacity * 2;
95294 if (newFramesCap < totalFramesRead + framesJustRead) {
95295 newFramesCap = totalFramesRead + framesJustRead;
95296 }
95297 oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(ma_int16);
95298 newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(ma_int16);
95299 if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) {
95300 break;
95301 }
95302 pNewFrames = (ma_int16*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
95303 if (pNewFrames == NULL) {
95304 ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
95305 break;
95306 }
95307 pFrames = pNewFrames;
95308 framesCapacity = newFramesCap;
95309 }
95310 MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(ma_int16)));
95311 totalFramesRead += framesJustRead;
95312 if (framesJustRead != framesToReadRightNow) {
95313 break;
95314 }
95315 }
95316 if (pConfig != NULL) {
95317 pConfig->channels = pMP3->channels;
95318 pConfig->sampleRate = pMP3->sampleRate;
95319 }
95320 ma_dr_mp3_uninit(pMP3);
95321 if (pTotalFrameCount) {
95322 *pTotalFrameCount = totalFramesRead;
95323 }
95324 return pFrames;
95325}
95326MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
95327{
95328 ma_dr_mp3 mp3;
95329 if (!ma_dr_mp3_init(&mp3, onRead, onSeek, onTell, NULL, pUserData, pAllocationCallbacks)) {
95330 return NULL;
95331 }
95332 return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
95333}
95334MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
95335{
95336 ma_dr_mp3 mp3;
95337 if (!ma_dr_mp3_init(&mp3, onRead, onSeek, onTell, NULL, pUserData, pAllocationCallbacks)) {
95338 return NULL;
95339 }
95340 return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
95341}
95342MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
95343{
95344 ma_dr_mp3 mp3;
95345 if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
95346 return NULL;
95347 }
95348 return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
95349}
95350MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
95351{
95352 ma_dr_mp3 mp3;
95353 if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
95354 return NULL;
95355 }
95356 return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
95357}
95358#ifndef MA_DR_MP3_NO_STDIO
95359MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
95360{
95361 ma_dr_mp3 mp3;
95362 if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
95363 return NULL;
95364 }
95365 return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
95366}
95367MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
95368{
95369 ma_dr_mp3 mp3;
95370 if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
95371 return NULL;
95372 }
95373 return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
95374}
95375#endif
95376MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
95377{
95378 if (pAllocationCallbacks != NULL) {
95379 return ma_dr_mp3__malloc_from_callbacks(sz, pAllocationCallbacks);
95380 } else {
95381 return ma_dr_mp3__malloc_default(sz, NULL);
95382 }
95383}
95384MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
95385{
95386 if (pAllocationCallbacks != NULL) {
95387 ma_dr_mp3__free_from_callbacks(p, pAllocationCallbacks);
95388 } else {
95389 ma_dr_mp3__free_default(p, NULL);
95390 }
95391}
95392#endif
95393/* dr_mp3_c end */
95394#endif /* MA_DR_MP3_IMPLEMENTATION */
95395#endif /* MA_NO_MP3 */
95396
95397
95398/* End globally disabled warnings. */
95399#if defined(_MSC_VER)
95400 #pragma warning(pop)
95401#endif
95402
95403#endif /* miniaudio_c */
95404#endif /* MINIAUDIO_IMPLEMENTATION */
95405
95406
95407/*
95408This software is available as a choice of the following licenses. Choose
95409whichever you prefer.
95410
95411===============================================================================
95412ALTERNATIVE 1 - Public Domain (www.unlicense.org)
95413===============================================================================
95414This is free and unencumbered software released into the public domain.
95415
95416Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
95417software, either in source code form or as a compiled binary, for any purpose,
95418commercial or non-commercial, and by any means.
95419
95420In jurisdictions that recognize copyright laws, the author or authors of this
95421software dedicate any and all copyright interest in the software to the public
95422domain. We make this dedication for the benefit of the public at large and to
95423the detriment of our heirs and successors. We intend this dedication to be an
95424overt act of relinquishment in perpetuity of all present and future rights to
95425this software under copyright law.
95426
95427THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
95428IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
95429FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
95430AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
95431ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
95432WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
95433
95434For more information, please refer to <http://unlicense.org/>
95435
95436===============================================================================
95437ALTERNATIVE 2 - MIT No Attribution
95438===============================================================================
95439Copyright 2025 David Reid
95440
95441Permission is hereby granted, free of charge, to any person obtaining a copy of
95442this software and associated documentation files (the "Software"), to deal in
95443the Software without restriction, including without limitation the rights to
95444use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
95445of the Software, and to permit persons to whom the Software is furnished to do
95446so.
95447
95448THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
95449IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
95450FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
95451AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
95452LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
95453OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
95454SOFTWARE.
95455*/
std::vector< std::string > extension
Definition AudioSequenceFactory.cpp:6
static struct @357330042345221051161372362127345240227027135012 audio
static const char * frames[]
Definition Mole.cpp:246
static void unused(void)
Definition alBnkfNew.c:60
@ A
Definition common_structs.h:29
#define j
s32 padding[2048]
Definition main.c:148
double sin(double)
#define MA_SOUND_SOURCE_CHANNEL_COUNT
Definition miniaudio.h:11184
MA_API ma_result ma_rb_acquire_read(ma_rb *pRB, size_t *pSizeInBytes, void **ppBufferOut)
MA_API ma_result ma_engine_node_init(const ma_engine_node_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_engine_node *pEngineNode)
MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref *pAudioBufferRef, void *pFramesOut, ma_uint64 frameCount, ma_bool32 loop)
ma_dither_mode
Definition miniaudio.h:4276
@ ma_dither_mode_none
Definition miniaudio.h:4277
@ ma_dither_mode_triangle
Definition miniaudio.h:4279
@ ma_dither_mode_rectangle
Definition miniaudio.h:4278
MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config *pConfig, void *pHeap, ma_bpf *pBPF)
MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager *pResourceManager, const char *pFilePath)
MA_API ma_engine_config ma_engine_config_init(void)
#define MA_FALLTHROUGH
Definition miniaudio.h:3949
MA_API void ma_delay_node_set_wet(ma_delay_node *pDelayNode, float value)
MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32 *pDst, const ma_int64 *pSrc, ma_uint64 count, float volume)
struct ma_resource_manager_data_buffer ma_resource_manager_data_buffer
Definition miniaudio.h:10291
MA_API void ma_delay_node_set_decay(ma_delay_node *pDelayNode, float value)
pthread_t ma_pthread_t
Definition miniaudio.h:3887
MA_API ma_vec3f ma_sound_get_position(const ma_sound *pSound)
MA_API void ma_copy_pcm_frames(void *dst, const void *src, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2 *pHPF, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
MA_API void ma_sound_set_volume(ma_sound *pSound, float volume)
MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16 *pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
MA_API ma_result ma_sound_group_init_ex(ma_engine *pEngine, const ma_sound_group_config *pConfig, ma_sound_group *pGroup)
#define MA_MAX_NODE_LOCAL_BUS_COUNT
Definition miniaudio.h:10608
MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format)
void * ma_proc
Definition miniaudio.h:3842
MA_API void ma_clip_pcm_frames(void *pDst, const void *pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source *pDataSource, float *pCursor)
#define MA_VERSION_STRING
Definition miniaudio.h:3754
MA_API ma_result ma_notch2_reinit(const ma_notch2_config *pConfig, ma_notch2 *pFilter)
MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter *pConverter, ma_channel *pChannelMap, size_t channelMapCap)
#define MA_MAX_NODE_BUS_COUNT
Definition miniaudio.h:10603
MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config *pConfig, size_t *pHeapSizeInBytes)
struct ma_resource_manager_data_buffer_node ma_resource_manager_data_buffer_node
Definition miniaudio.h:10290
#define NULL
Definition miniaudio.h:3851
MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2 *pBPF)
MA_API void ma_sound_set_velocity(ma_sound *pSound, float x, float y, float z)
pthread_cond_t ma_pthread_cond_t
Definition miniaudio.h:3889
MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config *pConfig, size_t *pHeapSizeInBytes)
MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
MA_API void ma_device_uninit(ma_device *pDevice)
MA_API ma_result ma_vfs_seek(ma_vfs *pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
ma_result(* ma_decoder_read_proc)(ma_decoder *pDecoder, void *pBufferOut, size_t bytesToRead, size_t *pBytesRead)
Definition miniaudio.h:9958
MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer *pDataBuffer, ma_uint64 frameIndex)
MA_API ma_result ma_fence_release(ma_fence *pFence)
MA_API ma_result ma_device_get_master_volume_db(ma_device *pDevice, float *pGainDB)
MA_API ma_result ma_data_converter_set_rate(ma_data_converter *pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
MA_API float ma_sound_group_get_max_distance(const ma_sound_group *pGroup)
MA_API void * ma_realloc(void *p, size_t sz, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group *pGroup, ma_uint32 listenerIndex)
MA_API void ma_delay_node_set_dry(ma_delay_node *pDelayNode, float value)
MA_API ma_result ma_vfs_write(ma_vfs *pVFS, ma_vfs_file file, const void *pSrc, size_t sizeInBytes, size_t *pBytesWritten)
MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder *pDecoder, ma_uint64 *pCursor)
MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config *pConfig, size_t *pHeapSizeInBytes)
MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
MA_API void ma_loshelf_node_uninit(ma_loshelf_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_result ma_bpf2_init(const ma_bpf2_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_bpf2 *pBPF)
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)
static MA_INLINE float * ma_offset_pcm_frames_ptr_f32(float *p, ma_uint64 offsetInFrames, ma_uint32 channels)
Definition miniaudio.h:9770
MA_API void ma_pcm_u8_to_s16(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb *pRB)
MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound *pSound)
MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1 *pHPF, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
MA_API ma_device * ma_engine_get_device(ma_engine *pEngine)
MA_API void ma_sound_set_max_distance(ma_sound *pSound, float maxDistance)
MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder *pDecoder, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead)
MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group *pGroup)
MA_API size_t ma_channel_map_to_string(const ma_channel *pChannelMap, ma_uint32 channels, char *pBufferOut, size_t bufferCap)
MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound *pSound)
MA_API ma_result ma_node_set_output_bus_volume(ma_node *pNode, ma_uint32 outputBusIndex, float volume)
ma_result(* ma_decoder_tell_proc)(ma_decoder *pDecoder, ma_int64 *pCursor)
Definition miniaudio.h:9960
MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config *pConfig, void *pHeap, ma_job_queue *pQueue)
MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels)
MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1 *pLPF)
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)
MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer *pDataBuffer, ma_uint64 *pLength)
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)
MA_API void ma_waveform_uninit(ma_waveform *pWaveform)
MA_API void ma_mutex_unlock(ma_mutex *pMutex)
MA_API ma_result ma_log_unregister_callback(ma_log *pLog, ma_log_callback callback)
MA_API void ma_copy_and_apply_volume_factor_f32(float *pSamplesOut, const float *pSamplesIn, ma_uint64 sampleCount, float factor)
MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener *pListener, float x, float y, float z)
ma_data_source *(* ma_data_source_get_next_proc)(ma_data_source *pDataSource)
Definition miniaudio.h:5828
MA_API ma_result ma_engine_set_time(ma_engine *pEngine, ma_uint64 globalTime)
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)
MA_API ma_result ma_hpf_reinit(const ma_hpf_config *pConfig, ma_hpf *pHPF)
MA_API void ma_sound_group_set_volume(ma_sound_group *pGroup, float volume)
MA_API void ma_lpf_node_uninit(ma_lpf_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32 *pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
void ma_node
Definition miniaudio.h:10625
MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config *pConfig, size_t *pHeapSizeInBytes)
MA_API void ma_blend_f32(float *pOut, float *pInA, float *pInB, float factor, ma_uint32 channels)
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)
MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel *pChannelMap, ma_uint32 channels)
MA_API ma_result ma_fence_wait(ma_fence *pFence)
MA_API void * ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb *pRB, ma_uint32 subbufferIndex, void *pBuffer)
#define MA_PRIVATE
Definition miniaudio.h:4025
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)
MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2 *pFilter, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer *pGainer, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
MA_API float ma_sound_group_get_min_distance(const ma_sound_group *pGroup)
MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream *pDataStream, ma_bool32 isLooping)
MA_API ma_result ma_spatializer_init(const ma_spatializer_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_spatializer *pSpatializer)
MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf *pBPF)
MA_API ma_result ma_noise_init(const ma_noise_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_noise *pNoise)
MA_API ma_result ma_log_postv(ma_log *pLog, ma_uint32 level, const char *pFormat, va_list args)
MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group *pGroup, ma_uint64 absoluteGlobalTimeInFrames)
MA_API void ma_pulsewave_uninit(ma_pulsewave *pWaveform)
void * ma_ptr
Definition miniaudio.h:3831
MA_API ma_result ma_fence_init(ma_fence *pFence)
MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config *pConfig, void *pHeap, ma_bpf2 *pBPF)
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)
MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref *pAudioBufferRef, ma_uint64 *pCursor)
MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source *pDataSource, ma_uint64 *pLoopBegInFrames, ma_uint64 *pLoopEndInFrames)
MA_API float ma_panner_get_pan(const ma_panner *pPanner)
MA_API ma_result ma_gainer_set_gains(ma_gainer *pGainer, float *pNewGains)
MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer *pDataBuffer, ma_uint64 *pCursor)
MA_API ma_result ma_audio_buffer_map(ma_audio_buffer *pAudioBuffer, void **ppFramesOut, ma_uint64 *pFrameCount)
MA_API void ma_pcm_convert(void *pOut, ma_format formatOut, const void *pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source *pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames)
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)
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)
#define MA_API
Definition miniaudio.h:4017
struct ma_lpf1_config ma_lpf2_config
MA_API ma_result ma_sound_get_length_in_seconds(ma_sound *pSound, float *pLength)
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)
MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer *pSpatializer)
MA_API ma_result ma_engine_init(const ma_engine_config *pConfig, ma_engine *pEngine)
#define MA_SIMD_ALIGNMENT
Definition miniaudio.h:4031
MA_API void ma_node_graph_uninit(ma_node_graph *pNodeGraph, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API void ma_peak_node_uninit(ma_peak_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
ma_result(* ma_encoder_write_pcm_frames_proc)(ma_encoder *pEncoder, const void *pFramesIn, ma_uint64 frameCount, ma_uint64 *pFramesWritten)
Definition miniaudio.h:10107
MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2 *pLPF)
MA_API float ma_volume_db_to_linear(float gain)
MA_API ma_result ma_context_uninit(ma_context *pContext)
ma_uint8 ma_channel
Definition miniaudio.h:4111
MA_API void ma_biquad_node_uninit(ma_biquad_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config *pConfig, ma_lpf1 *pLPF)
void(* ma_encoder_uninit_proc)(ma_encoder *pEncoder)
Definition miniaudio.h:10106
MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener *pListener)
MA_API float ma_sound_get_max_gain(const ma_sound *pSound)
MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound *pSound, ma_uint64 absoluteGlobalTimeInFrames)
#define MA_ATOMIC(alignment, type)
Definition miniaudio.h:4104
MA_API ma_result ma_sound_start(ma_sound *pSound)
MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_channel_converter *pConverter)
MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer *pDataBuffer)
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)
MA_API ma_node_graph * ma_node_get_node_graph(const ma_node *pNode)
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)
MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine *pEngine, ma_uint32 listenerIndex)
pthread_mutex_t ma_pthread_mutex_t
Definition miniaudio.h:3888
MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound *pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames)
MA_API ma_result ma_engine_play_sound_ex(ma_engine *pEngine, const char *pFilePath, ma_node *pNode, ma_uint32 nodeInputBusIndex)
MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
MA_API ma_bool32 ma_context_is_loopback_supported(ma_context *pContext)
MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2 *pLPF, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
struct ma_peak2_config ma_peak_config
MA_API const char * ma_get_backend_name(ma_backend backend)
MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source *pDataSource, ma_uint64 frameIndex)
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)
MA_API ma_context_config ma_context_config_init(void)
MA_API ma_result ma_bpf_init(const ma_bpf_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_bpf *pBPF)
MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config *pConfig, ma_hishelf2 *pFilter)
MA_API ma_log * ma_device_get_log(ma_device *pDevice)
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)
MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group *pGroup)
MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2 *pFilter, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
MA_API void ma_encoder_uninit(ma_encoder *pEncoder)
MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave *pWaveform, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead)
MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate)
ma_sound ma_sound_group
Definition miniaudio.h:11248
MA_API ma_result ma_data_source_init(const ma_data_source_config *pConfig, ma_data_source *pDataSource)
MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2 *pFilter)
MA_API ma_result ma_gainer_init(const ma_gainer_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_gainer *pGainer)
MA_API void ma_apply_volume_factor_s16(ma_int16 *pSamples, ma_uint64 sampleCount, float factor)
MA_API void ma_peak2_uninit(ma_peak2 *pFilter, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API void ma_hishelf2_uninit(ma_hishelf2 *pFilter, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API void ma_sound_group_set_doppler_factor(ma_sound_group *pGroup, float dopplerFactor)
MA_API ma_uint64 ma_engine_get_time(const ma_engine *pEngine)
void ma_async_notification
Definition miniaudio.h:6265
MA_API ma_result ma_decoder_get_available_frames(ma_decoder *pDecoder, ma_uint64 *pAvailableFrames)
MA_API ma_result ma_peak2_reinit(const ma_peak2_config *pConfig, ma_peak2 *pFilter)
MA_API ma_result ma_device_get_master_volume(ma_device *pDevice, float *pVolume)
MA_API void ma_engine_listener_get_cone(const ma_engine *pEngine, ma_uint32 listenerIndex, float *pInnerAngleInRadians, float *pOuterAngleInRadians, float *pOuterGain)
ma_uint32 ma_uintptr
Definition miniaudio.h:3818
MA_API void ma_spatializer_set_attenuation_model(ma_spatializer *pSpatializer, ma_attenuation_model attenuationModel)
MA_API float ma_spatializer_get_max_gain(const ma_spatializer *pSpatializer)
MA_API ma_engine * ma_sound_group_get_engine(const ma_sound_group *pGroup)
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)
MA_API ma_result ma_resource_manager_process_job(ma_resource_manager *pResourceManager, ma_job *pJob)
struct ma_node_base ma_node_base
Definition miniaudio.h:10744
MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config *pConfig, ma_hishelf_node *pNode)
ma_engine_node_type
Definition miniaudio.h:11121
@ ma_engine_node_type_group
Definition miniaudio.h:11123
@ ma_engine_node_type_sound
Definition miniaudio.h:11122
MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener *pListener, float x, float y, float z)
MA_API ma_result ma_vfs_tell(ma_vfs *pVFS, ma_vfs_file file, ma_int64 *pCursor)
MA_API ma_result ma_default_vfs_init(ma_default_vfs *pVFS, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API const char * ma_version_string(void)
MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_hishelf2 *pFilter)
MA_API ma_result ma_rb_commit_read(ma_rb *pRB, size_t sizeInBytes)
MA_API ma_result ma_sound_group_stop(ma_sound_group *pGroup)
MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer *pSpatializer)
MA_API void ma_pcm_s32_to_s24(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound *pSound)
unsigned char ma_uint8
Definition miniaudio.h:3791
MA_API ma_result ma_engine_stop(ma_engine *pEngine)
MA_API void ma_data_converter_uninit(ma_data_converter *pConverter, const ma_allocation_callbacks *pAllocationCallbacks)
ma_resample_algorithm
Definition miniaudio.h:5379
@ ma_resample_algorithm_linear
Definition miniaudio.h:5380
@ ma_resample_algorithm_custom
Definition miniaudio.h:5381
MA_API ma_result ma_data_source_seek_to_second(ma_data_source *pDataSource, float seekPointInSeconds)
MA_API void ma_sound_group_get_cone(const ma_sound_group *pGroup, float *pInnerAngleInRadians, float *pOuterAngleInRadians, float *pOuterGain)
MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group *pGroup)
MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source *pDataSource, float *pLength)
MA_API ma_node_graph * ma_engine_get_node_graph(ma_engine *pEngine)
MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config *pConfig, void *pHeap, ma_spatializer_listener *pListener)
MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager *pResourceManager, const char *pName)
MA_API void ma_pcm_s24_to_s32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config *pConfig, void *pHeap, ma_loshelf2 *pFilter)
MA_API void ma_resource_manager_uninit(ma_resource_manager *pResourceManager)
MA_API void ma_clip_samples_f32(float *pDst, const float *pSrc, ma_uint64 count)
MA_API void ma_data_source_uninit(ma_data_source *pDataSource)
MA_API void ma_pcm_s16_to_u8(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
struct ma_context_config ma_context_config
Definition miniaudio.h:7056
MA_API ma_node_config ma_node_config_init(void)
MA_API ma_result ma_waveform_set_type(ma_waveform *pWaveform, ma_waveform_type type)
MA_API ma_log * ma_context_get_log(ma_context *pContext)
MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor *pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
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)
MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter *pConverter, ma_uint64 inputFrameCount, ma_uint64 *pOutputFrameCount)
MA_API ma_uint32 ma_node_get_output_channels(const ma_node *pNode, ma_uint32 outputBusIndex)
MA_API float ma_volume_linear_to_db(float factor)
float ma_float
Definition miniaudio.h:3827
MA_API float ma_delay_get_decay(const ma_delay *pDelay)
MA_API ma_result ma_sound_init_ex(ma_engine *pEngine, const ma_sound_config *pConfig, ma_sound *pSound)
ma_ios_session_category_option
Definition miniaudio.h:6930
@ ma_ios_session_category_option_default_to_speaker
Definition miniaudio.h:6934
@ ma_ios_session_category_option_allow_air_play
Definition miniaudio.h:6937
@ ma_ios_session_category_option_duck_others
Definition miniaudio.h:6932
@ ma_ios_session_category_option_mix_with_others
Definition miniaudio.h:6931
@ ma_ios_session_category_option_allow_bluetooth_a2dp
Definition miniaudio.h:6936
@ ma_ios_session_category_option_allow_bluetooth
Definition miniaudio.h:6933
@ ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others
Definition miniaudio.h:6935
MA_API ma_bool32 ma_device_is_started(const ma_device *pDevice)
MA_API ma_result ma_noise_get_heap_size(const ma_noise_config *pConfig, size_t *pHeapSizeInBytes)
MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source *pDataSource, ma_uint64 frameIndex)
MA_API void ma_pcm_rb_reset(ma_pcm_rb *pRB)
MA_API void ma_apply_volume_factor_s24(void *pSamples, ma_uint64 sampleCount, float factor)
MA_API void ma_spatializer_set_max_distance(ma_spatializer *pSpatializer, float maxDistance)
MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer *pAudioBuffer, void *pFramesOut, ma_uint64 frameCount, ma_bool32 loop)
MA_API ma_result ma_resampler_reset(ma_resampler *pResampler)
MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener *pListener, ma_bool32 isEnabled)
MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer *pDataBuffer)
MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config *pConfig, size_t *pHeapSizeInBytes)
MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
#define MA_MAX_FILTER_ORDER
Definition miniaudio.h:4261
ma_performance_profile
Definition miniaudio.h:4347
@ ma_performance_profile_low_latency
Definition miniaudio.h:4348
@ ma_performance_profile_conservative
Definition miniaudio.h:4349
MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut)
MA_API ma_result ma_spinlock_lock(volatile ma_spinlock *pSpinlock)
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)
MA_API ma_data_converter_config ma_data_converter_config_init_default(void)
ma_handedness
Definition miniaudio.h:5144
@ ma_handedness_left
Definition miniaudio.h:5146
@ ma_handedness_right
Definition miniaudio.h:5145
ma_ios_session_category
Definition miniaudio.h:6917
@ ma_ios_session_category_play_and_record
Definition miniaudio.h:6924
@ ma_ios_session_category_default
Definition miniaudio.h:6918
@ ma_ios_session_category_multi_route
Definition miniaudio.h:6925
@ ma_ios_session_category_playback
Definition miniaudio.h:6922
@ ma_ios_session_category_none
Definition miniaudio.h:6919
@ ma_ios_session_category_ambient
Definition miniaudio.h:6920
@ ma_ios_session_category_record
Definition miniaudio.h:6923
@ ma_ios_session_category_solo_ambient
Definition miniaudio.h:6921
MA_API ma_result ma_data_source_set_next_callback(ma_data_source *pDataSource, ma_data_source_get_next_proc onGetNext)
MA_API void ma_delay_node_uninit(ma_delay_node *pDelayNode, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API void ma_aligned_free(void *p, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API void ma_sound_set_min_gain(ma_sound *pSound, float minGain)
MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine *pEngine)
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)
MA_API void ma_pcm_s24_to_f32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API void ma_apply_volume_factor_s32(ma_int32 *pSamples, ma_uint64 sampleCount, float factor)
MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void)
MA_API void ma_job_queue_uninit(ma_job_queue *pQueue, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API void ma_clip_samples_s16(ma_int16 *pDst, const ma_int32 *pSrc, ma_uint64 count)
MA_API void ma_sound_get_cone(const ma_sound *pSound, float *pInnerAngleInRadians, float *pOuterAngleInRadians, float *pOuterGain)
MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener *pListener, float x, float y, float z)
MA_API ma_resource_manager_config ma_resource_manager_config_init(void)
MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer *pPagedAudioBuffer)
MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate)
MA_API ma_result ma_decoder_init_file_w(const wchar_t *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler *pResampler, ma_uint64 inputFrameCount, ma_uint64 *pOutputFrameCount)
MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref *pAudioBufferRef, ma_uint64 *pLength)
MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf *pHPF, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
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)
ma_result(* ma_encoder_seek_proc)(ma_encoder *pEncoder, ma_int64 offset, ma_seek_origin origin)
Definition miniaudio.h:10104
MA_API ma_result ma_get_enabled_backends(ma_backend *pBackends, size_t backendCap, size_t *pBackendCount)
MA_API void ma_hpf_node_uninit(ma_hpf_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer *pSpatializer, ma_spatializer_listener *pListener, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
MA_API ma_result ma_vfs_open_w(ma_vfs *pVFS, const wchar_t *pFilePath, ma_uint32 openMode, ma_vfs_file *pFile)
MA_API void ma_sound_group_set_pan_mode(ma_sound_group *pGroup, ma_pan_mode panMode)
MA_API void ma_bpf_node_uninit(ma_bpf_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler *pResampler)
MA_API const void * ma_offset_pcm_frames_const_ptr(const void *p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
MA_API ma_data_source * ma_data_source_get_current(const ma_data_source *pDataSource)
MA_API ma_result ma_decoder_init_vfs_w(ma_vfs *pVFS, const wchar_t *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config *pConfig, void *pHeap, ma_slot_allocator *pAllocator)
MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb *pRB, ma_uint32 sampleRate)
MA_API ma_uint64 ma_node_get_state_time(const ma_node *pNode, ma_node_state state)
MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream *pDataStream, ma_uint64 *pLength)
MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config *pConfig, size_t *pHeapSizeInBytes)
unsigned long long ma_uint64
Definition miniaudio.h:3808
MA_API void ma_engine_node_uninit(ma_engine_node *pEngineNode, const ma_allocation_callbacks *pAllocationCallbacks)
ma_job_queue_flags
Definition miniaudio.h:6528
@ MA_JOB_QUEUE_FLAG_NON_BLOCKING
Definition miniaudio.h:6529
MA_API ma_result ma_notch2_init(const ma_notch2_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_notch2 *pFilter)
MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group *pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)
MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler *pResampler)
MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config *pConfig, void *pHeap, ma_hpf *pLPF)
MA_API ma_engine * ma_sound_get_engine(const ma_sound *pSound)
MA_API ma_result ma_rb_acquire_write(ma_rb *pRB, size_t *pSizeInBytes, void **ppBufferOut)
MA_API ma_sound_config ma_sound_config_init_2(ma_engine *pEngine)
MA_API ma_result ma_encoder_init_vfs_w(ma_vfs *pVFS, const wchar_t *pFilePath, const ma_encoder_config *pConfig, ma_encoder *pEncoder)
void ma_resampling_backend
Definition miniaudio.h:5363
MA_API ma_result ma_job_queue_next(ma_job_queue *pQueue, ma_job *pJob)
MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity)
MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void)
MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2 *pFilter)
unsigned int ma_uint32
Definition miniaudio.h:3795
struct ma_node_graph ma_node_graph
Definition miniaudio.h:10624
MA_API void ma_mutex_uninit(ma_mutex *pMutex)
ma_result(* ma_seek_proc)(void *pUserData, ma_int64 offset, ma_seek_origin origin)
Definition miniaudio.h:9910
ma_device_type
Definition miniaudio.h:6902
@ ma_device_type_duplex
Definition miniaudio.h:6905
@ ma_device_type_playback
Definition miniaudio.h:6903
@ ma_device_type_capture
Definition miniaudio.h:6904
@ ma_device_type_loopback
Definition miniaudio.h:6906
MA_API ma_result ma_data_converter_init(const ma_data_converter_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_data_converter *pConverter)
MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager *pResourceManager, const wchar_t *pFilePath, ma_uint32 flags)
MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group *pGroup)
MA_API ma_log * ma_resource_manager_get_log(ma_resource_manager *pResourceManager)
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)
MA_API ma_node * ma_node_graph_get_endpoint(ma_node_graph *pNodeGraph)
MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound *pSound, ma_uint64 *pCursor)
MA_API ma_result ma_biquad_reinit(const ma_biquad_config *pConfig, ma_biquad *pBQ)
MA_API ma_result ma_mix_pcm_frames_f32(float *pDst, const float *pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume)
MA_API ma_result ma_async_notification_event_init(ma_async_notification_event *pNotificationEvent)
MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group *pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)
MA_API const char * ma_result_description(ma_result result)
MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb *pRB)
MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config *pConfig, void *pHeap, ma_linear_resampler *pResampler)
#define MA_CHANNEL_INDEX_NULL
Definition miniaudio.h:5691
MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event *pNotificationEvent)
MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator *pAllocator, ma_uint64 *pSlot)
MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer *pAudioBuffer)
MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine *pEngine, ma_uint32 listenerIndex)
MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave *pWaveform, double frequency)
MA_API void ma_biquad_uninit(ma_biquad *pBQ, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config *pConfig, ma_context *pContext)
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)
MA_API void ma_spatializer_get_cone(const ma_spatializer *pSpatializer, float *pInnerAngleInRadians, float *pOuterAngleInRadians, float *pOuterGain)
MA_API ma_result ma_rb_seek_read(ma_rb *pRB, size_t offsetInBytes)
#define MA_SIZEOF_PTR
Definition miniaudio.h:3773
ma_result(* ma_job_proc)(ma_job *pJob)
Definition miniaudio.h:6372
MA_API ma_result ma_lpf1_clear_cache(ma_lpf1 *pLPF)
MA_API void ma_sound_set_direction(ma_sound *pSound, float x, float y, float z)
MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll *pNotificationPoll)
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)
void(* ma_device_notification_proc)(const ma_device_notification *pNotification)
Definition miniaudio.h:6832
MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock *pSpinlock)
MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler *pResampler, const void *pFramesIn, ma_uint64 *pFrameCountIn, void *pFramesOut, ma_uint64 *pFrameCountOut)
MA_API float ma_engine_get_volume(ma_engine *pEngine)
MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer *pSpatializer, float volume)
MA_API ma_result ma_sound_set_end_callback(ma_sound *pSound, ma_sound_end_proc callback, void *pUserData)
MA_API void ma_spatializer_set_min_gain(ma_spatializer *pSpatializer, float minGain)
MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1 *pHPF)
MA_API void ma_notch_node_uninit(ma_notch_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group *pGroup)
MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound *pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames)
MA_API ma_result ma_log_postf(ma_log *pLog, ma_uint32 level, const char *pFormat,...) MA_ATTRIBUTE_FORMAT(3
ma_result(* ma_tell_proc)(void *pUserData, ma_int64 *pCursor)
Definition miniaudio.h:9911
ma_uint32 ma_bool32
Definition miniaudio.h:3822
MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)
MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config *pConfig, ma_biquad_node *pNode)
MA_API void ma_notch2_uninit(ma_notch2 *pFilter, const ma_allocation_callbacks *pAllocationCallbacks)
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)
#define MA_MIN_CHANNELS
Definition miniaudio.h:4255
MA_API ma_result ma_job_process(ma_job *pJob)
MA_API ma_result ma_decoder_init_memory(const void *pData, size_t dataSize, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine *pEngine, ma_uint32 listenerIndex)
MA_API ma_result ma_rb_commit_write(ma_rb *pRB, size_t sizeInBytes)
MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config *pConfig, ma_hpf_node *pNode)
MA_API ma_result ma_hpf2_init(const ma_hpf2_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_hpf2 *pHPF)
MA_API void ma_pcm_s32_to_u8(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder *pEncoder, const void *pFramesIn, ma_uint64 frameCount, ma_uint64 *pFramesWritten)
MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream *pDataStream)
MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll *pNotificationPoll)
MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config *pConfig, void *pHeap, ma_engine_node *pEngineNode)
MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine *pEngine, ma_uint32 listenerIndex)
MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound *pSound, ma_uint64 fadeLengthInFrames)
MA_API ma_result ma_decoder_init_vfs(ma_vfs *pVFS, const char *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
MA_API void ma_sound_group_set_max_distance(ma_sound_group *pGroup, float maxDistance)
MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter *pConverter, float ratioInOut)
ma_pan_mode
Definition miniaudio.h:5054
@ ma_pan_mode_balance
Definition miniaudio.h:5055
@ ma_pan_mode_pan
Definition miniaudio.h:5056
MA_API ma_result ma_mutex_init(ma_mutex *pMutex)
MA_API void ma_silence_pcm_frames(void *p, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
MA_API ma_node_state ma_node_get_state_by_time(const ma_node *pNode, ma_uint64 globalTime)
ma_aaudio_content_type
Definition miniaudio.h:6995
@ ma_aaudio_content_type_music
Definition miniaudio.h:6998
@ ma_aaudio_content_type_speech
Definition miniaudio.h:6997
@ ma_aaudio_content_type_default
Definition miniaudio.h:6996
@ ma_aaudio_content_type_sonification
Definition miniaudio.h:7000
@ ma_aaudio_content_type_movie
Definition miniaudio.h:6999
MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels)
MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave *pWaveform, double dutyCycle)
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)
MA_API ma_result ma_vfs_info(ma_vfs *pVFS, ma_vfs_file file, ma_file_info *pInfo)
MA_API void ma_apply_volume_factor_pcm_frames(void *pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor)
MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source *pDataSource)
MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
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)
MA_API ma_result ma_engine_read_pcm_frames(ma_engine *pEngine, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead)
MA_API ma_bool32 ma_device_id_equal(const ma_device_id *pA, const ma_device_id *pB)
MA_API ma_result ma_hpf1_init(const ma_hpf1_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_hpf1 *pHPF)
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)
MA_API void ma_audio_buffer_uninit(ma_audio_buffer *pAudioBuffer)
MA_API ma_result ma_notch_node_reinit(const ma_notch_config *pConfig, ma_notch_node *pNode)
MA_API ma_result ma_lpf_clear_cache(ma_lpf *pLPF)
MA_API void ma_fence_uninit(ma_fence *pFence)
ma_handle ma_vfs_file
Definition miniaudio.h:9857
ma_thread_priority
Definition miniaudio.h:4430
@ ma_thread_priority_default
Definition miniaudio.h:4438
@ ma_thread_priority_realtime
Definition miniaudio.h:4437
@ ma_thread_priority_high
Definition miniaudio.h:4435
@ ma_thread_priority_lowest
Definition miniaudio.h:4432
@ ma_thread_priority_low
Definition miniaudio.h:4433
@ ma_thread_priority_highest
Definition miniaudio.h:4436
@ ma_thread_priority_idle
Definition miniaudio.h:4431
@ ma_thread_priority_normal
Definition miniaudio.h:4434
MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
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)
MA_API ma_result ma_job_queue_post(ma_job_queue *pQueue, const ma_job *pJob)
MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter *pConverter)
MA_API void ma_apply_volume_factor_f32(float *pSamples, ma_uint64 sampleCount, float factor)
MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound *pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds)
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)
signed char ma_int8
Definition miniaudio.h:3790
MA_API ma_result ma_device_stop(ma_device *pDevice)
MA_API void ma_mutex_lock(ma_mutex *pMutex)
#define MA_ENGINE_MAX_LISTENERS
Definition miniaudio.h:11115
ma_result(* ma_read_proc)(void *pUserData, void *pBufferOut, size_t bytesToRead, size_t *pBytesRead)
Definition miniaudio.h:9909
MA_API void ma_sound_group_set_rolloff(ma_sound_group *pGroup, float rolloff)
MA_API ma_bool32 ma_sound_is_playing(const ma_sound *pSound)
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)
ma_mono_expansion_mode
Definition miniaudio.h:5517
@ ma_mono_expansion_mode_stereo_only
Definition miniaudio.h:5520
@ ma_mono_expansion_mode_average
Definition miniaudio.h:5519
@ ma_mono_expansion_mode_default
Definition miniaudio.h:5521
@ ma_mono_expansion_mode_duplicate
Definition miniaudio.h:5518
MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2 *pHPF)
MA_API ma_result ma_encoder_init_file(const char *pFilePath, const ma_encoder_config *pConfig, ma_encoder *pEncoder)
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)
MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound *pSound)
MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb *pRB)
MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config *pConfig, size_t *pHeapSizeInBytes)
MA_API ma_decoder_config ma_decoder_config_init_default(void)
MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager *pResourceManager)
MA_API void ma_loshelf2_uninit(ma_loshelf2 *pFilter, const ma_allocation_callbacks *pAllocationCallbacks)
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)
MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb *pRB)
MA_API ma_result ma_data_source_set_next(ma_data_source *pDataSource, ma_data_source *pNextDataSource)
ma_uint8 ma_bool8
Definition miniaudio.h:3821
MA_API void ma_spatializer_set_positioning(ma_spatializer *pSpatializer, ma_positioning positioning)
MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group *pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)
MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter *pConverter, ma_uint64 outputFrameCount, ma_uint64 *pInputFrameCount)
MA_API void * ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_result ma_noise_read_pcm_frames(ma_noise *pNoise, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead)
MA_API ma_result ma_event_wait(ma_event *pEvent)
MA_API void ma_sound_set_fade_in_milliseconds(ma_sound *pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)
MA_API ma_result ma_device_get_info(ma_device *pDevice, ma_device_type type, ma_device_info *pDeviceInfo)
MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer *pSpatializer)
MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer *pDataBuffer)
MA_API ma_result ma_log_init(const ma_allocation_callbacks *pAllocationCallbacks, ma_log *pLog)
MA_API ma_device_state ma_device_get_state(const ma_device *pDevice)
MA_API float ma_sound_get_min_gain(const ma_sound *pSound)
MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener *pListener, float speedOfSound)
MA_API ma_result ma_node_set_state_time(ma_node *pNode, ma_node_state state, ma_uint64 globalTime)
MA_API void ma_pcm_u8_to_s24(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API void ma_channel_map_copy_or_default(ma_channel *pOut, size_t channelMapCapOut, const ma_channel *pIn, ma_uint32 channels)
_ma_channel_position
Definition miniaudio.h:4113
@ MA_CHANNEL_MONO
Definition miniaudio.h:4115
@ MA_CHANNEL_BACK_LEFT
Definition miniaudio.h:4120
@ MA_CHANNEL_AUX_6
Definition miniaudio.h:4140
@ MA_CHANNEL_FRONT_LEFT_CENTER
Definition miniaudio.h:4122
@ MA_CHANNEL_AUX_29
Definition miniaudio.h:4163
@ MA_CHANNEL_AUX_18
Definition miniaudio.h:4152
@ MA_CHANNEL_AUX_21
Definition miniaudio.h:4155
@ MA_CHANNEL_AUX_15
Definition miniaudio.h:4149
@ MA_CHANNEL_BACK_CENTER
Definition miniaudio.h:4124
@ MA_CHANNEL_AUX_3
Definition miniaudio.h:4137
@ MA_CHANNEL_AUX_9
Definition miniaudio.h:4143
@ MA_CHANNEL_AUX_28
Definition miniaudio.h:4162
@ MA_CHANNEL_AUX_2
Definition miniaudio.h:4136
@ MA_CHANNEL_AUX_14
Definition miniaudio.h:4148
@ MA_CHANNEL_FRONT_RIGHT_CENTER
Definition miniaudio.h:4123
@ MA_CHANNEL_LFE
Definition miniaudio.h:4119
@ MA_CHANNEL_TOP_FRONT_LEFT
Definition miniaudio.h:4128
@ MA_CHANNEL_AUX_20
Definition miniaudio.h:4154
@ MA_CHANNEL_TOP_CENTER
Definition miniaudio.h:4127
@ MA_CHANNEL_AUX_1
Definition miniaudio.h:4135
@ MA_CHANNEL_AUX_25
Definition miniaudio.h:4159
@ MA_CHANNEL_AUX_5
Definition miniaudio.h:4139
@ MA_CHANNEL_TOP_BACK_RIGHT
Definition miniaudio.h:4133
@ MA_CHANNEL_TOP_FRONT_CENTER
Definition miniaudio.h:4129
@ MA_CHANNEL_AUX_27
Definition miniaudio.h:4161
@ MA_CHANNEL_FRONT_RIGHT
Definition miniaudio.h:4117
@ MA_CHANNEL_AUX_13
Definition miniaudio.h:4147
@ MA_CHANNEL_NONE
Definition miniaudio.h:4114
@ MA_CHANNEL_AUX_7
Definition miniaudio.h:4141
@ MA_CHANNEL_BACK_RIGHT
Definition miniaudio.h:4121
@ MA_CHANNEL_AUX_24
Definition miniaudio.h:4158
@ MA_CHANNEL_AUX_30
Definition miniaudio.h:4164
@ MA_CHANNEL_FRONT_LEFT
Definition miniaudio.h:4116
@ MA_CHANNEL_AUX_12
Definition miniaudio.h:4146
@ MA_CHANNEL_AUX_31
Definition miniaudio.h:4165
@ MA_CHANNEL_AUX_17
Definition miniaudio.h:4151
@ MA_CHANNEL_SIDE_LEFT
Definition miniaudio.h:4125
@ MA_CHANNEL_AUX_8
Definition miniaudio.h:4142
@ MA_CHANNEL_LEFT
Definition miniaudio.h:4166
@ MA_CHANNEL_AUX_0
Definition miniaudio.h:4134
@ MA_CHANNEL_AUX_19
Definition miniaudio.h:4153
@ MA_CHANNEL_AUX_23
Definition miniaudio.h:4157
@ MA_CHANNEL_TOP_BACK_CENTER
Definition miniaudio.h:4132
@ MA_CHANNEL_FRONT_CENTER
Definition miniaudio.h:4118
@ MA_CHANNEL_TOP_FRONT_RIGHT
Definition miniaudio.h:4130
@ MA_CHANNEL_AUX_10
Definition miniaudio.h:4144
@ MA_CHANNEL_POSITION_COUNT
Definition miniaudio.h:4168
@ MA_CHANNEL_SIDE_RIGHT
Definition miniaudio.h:4126
@ MA_CHANNEL_AUX_22
Definition miniaudio.h:4156
@ MA_CHANNEL_AUX_4
Definition miniaudio.h:4138
@ MA_CHANNEL_TOP_BACK_LEFT
Definition miniaudio.h:4131
@ MA_CHANNEL_AUX_16
Definition miniaudio.h:4150
@ MA_CHANNEL_AUX_11
Definition miniaudio.h:4145
@ MA_CHANNEL_AUX_26
Definition miniaudio.h:4160
@ MA_CHANNEL_RIGHT
Definition miniaudio.h:4167
MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer *pAudioBuffer, ma_uint64 frameIndex)
MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config *pConfig, void *pHeap, ma_resampler *pResampler)
MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer *pSpatializer)
MA_API ma_result ma_resource_manager_post_job(ma_resource_manager *pResourceManager, const ma_job *pJob)
MA_API const char * ma_channel_position_to_string(ma_channel channel)
MA_API void ma_clip_samples_s32(ma_int32 *pDst, const ma_int64 *pSrc, ma_uint64 count)
MA_API ma_log * ma_engine_get_log(ma_engine *pEngine)
MA_API float ma_delay_node_get_dry(const ma_delay_node *pDelayNode)
MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine *pEngine)
MA_API void ma_spatializer_set_direction(ma_spatializer *pSpatializer, float x, float y, float z)
MA_API ma_result ma_peak_node_reinit(const ma_peak_config *pConfig, ma_peak_node *pNode)
MA_API void ma_pcm_s32_to_f32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node *pDataSourceNode)
MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref *pAudioBufferRef)
MA_API void ma_pcm_s16_to_f32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source *pDataSource)
ma_channel_mix_mode
Definition miniaudio.h:4326
@ ma_channel_mix_mode_default
Definition miniaudio.h:4330
@ ma_channel_mix_mode_simple
Definition miniaudio.h:4328
@ ma_channel_mix_mode_rectangular
Definition miniaudio.h:4327
@ ma_channel_mix_mode_custom_weights
Definition miniaudio.h:4329
MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config *pConfig, void *pHeap, ma_hpf2 *pHPF)
MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref *pAudioBufferRef)
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)
unsigned short ma_uint16
Definition miniaudio.h:3793
MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph *pNodeGraph)
MA_API void ma_event_uninit(ma_event *pEvent)
MA_API void ma_fader_set_fade_ex(ma_fader *pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames)
MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound *pSound)
MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config *pConfig, ma_bpf_node *pNode)
MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_loshelf2 *pFilter)
#define MA_VERSION_MINOR
Definition miniaudio.h:3752
MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source *pDataSource, ma_bool32 isLooping)
MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames)
MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group *pGroup)
ma_log_level
Definition miniaudio.h:4074
@ MA_LOG_LEVEL_ERROR
Definition miniaudio.h:4078
@ MA_LOG_LEVEL_WARNING
Definition miniaudio.h:4077
@ MA_LOG_LEVEL_INFO
Definition miniaudio.h:4076
@ MA_LOG_LEVEL_DEBUG
Definition miniaudio.h:4075
MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config *pConfig, void *pHeap, ma_biquad *pBQ)
MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
MA_API ma_positioning ma_sound_get_positioning(const ma_sound *pSound)
MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config *pConfig, size_t *pHeapSizeInBytes)
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)
MA_API void ma_delay_set_wet(ma_delay *pDelay, float value)
MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source *pDataSource, ma_uint64 frameCount, ma_uint64 *pFramesSeeked)
MA_API ma_result ma_vfs_close(ma_vfs *pVFS, ma_vfs_file file)
MA_API ma_device_config ma_device_config_init(ma_device_type deviceType)
MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref *pAudioBufferRef, ma_uint64 frameCount)
MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf *pHPF)
MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8 *pDst, const ma_int64 *pSrc, ma_uint64 count, float volume)
MA_API float ma_delay_node_get_wet(const ma_delay_node *pDelayNode)
MA_API void ma_sound_group_set_velocity(ma_sound_group *pGroup, float x, float y, float z)
MA_API ma_result ma_node_graph_set_time(ma_node_graph *pNodeGraph, ma_uint64 globalTime)
MA_API void ma_spatializer_set_min_distance(ma_spatializer *pSpatializer, float minDistance)
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)
MA_API ma_resource_manager * ma_engine_get_resource_manager(ma_engine *pEngine)
MA_API ma_result ma_fence_acquire(ma_fence *pFence)
ma_share_mode
Definition miniaudio.h:6910
@ ma_share_mode_exclusive
Definition miniaudio.h:6912
@ ma_share_mode_shared
Definition miniaudio.h:6911
MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend)
MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel *pChannelMap, ma_channel channelPosition, ma_uint32 *pChannelIndex)
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)
MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config *pConfig, size_t *pHeapSizeInBytes)
MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void)
MA_API ma_result ma_node_detach_output_bus(ma_node *pNode, ma_uint32 outputBusIndex)
MA_API void ma_sound_set_pan_mode(ma_sound *pSound, ma_pan_mode panMode)
MA_API void ma_engine_listener_set_cone(ma_engine *pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
MA_API float ma_sound_get_doppler_factor(const ma_sound *pSound)
MA_API void ma_sound_set_rolloff(ma_sound *pSound, float rolloff)
MA_API float ma_sound_get_min_distance(const ma_sound *pSound)
MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter *pConverter)
ma_channel_conversion_path
Definition miniaudio.h:5507
@ ma_channel_conversion_path_mono_out
Definition miniaudio.h:5510
@ ma_channel_conversion_path_mono_in
Definition miniaudio.h:5511
@ ma_channel_conversion_path_passthrough
Definition miniaudio.h:5509
@ ma_channel_conversion_path_unknown
Definition miniaudio.h:5508
@ ma_channel_conversion_path_shuffle
Definition miniaudio.h:5512
@ ma_channel_conversion_path_weights
Definition miniaudio.h:5513
MA_API ma_result ma_lpf2_init(const ma_lpf2_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_lpf2 *pLPF)
MA_API void ma_sound_set_spatialization_enabled(ma_sound *pSound, ma_bool32 enabled)
MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void *pOptionalPreallocatedBuffer, const ma_allocation_callbacks *pAllocationCallbacks, ma_rb *pRB)
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)
MA_API ma_result ma_device_handle_backend_data_callback(ma_device *pDevice, void *pOutput, const void *pInput, ma_uint32 frameCount)
MA_API ma_result ma_async_notification_signal(ma_async_notification *pNotification)
MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf *pLPF, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source *pDataSource)
MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data *pData)
ma_backend
Definition miniaudio.h:6705
@ ma_backend_sndio
Definition miniaudio.h:6710
@ ma_backend_alsa
Definition miniaudio.h:6714
@ ma_backend_webaudio
Definition miniaudio.h:6718
@ ma_backend_winmm
Definition miniaudio.h:6708
@ ma_backend_pulseaudio
Definition miniaudio.h:6713
@ ma_backend_aaudio
Definition miniaudio.h:6716
@ ma_backend_opensl
Definition miniaudio.h:6717
@ ma_backend_oss
Definition miniaudio.h:6712
@ ma_backend_custom
Definition miniaudio.h:6719
@ ma_backend_coreaudio
Definition miniaudio.h:6709
@ ma_backend_wasapi
Definition miniaudio.h:6706
@ ma_backend_null
Definition miniaudio.h:6720
@ ma_backend_audio4
Definition miniaudio.h:6711
@ ma_backend_dsound
Definition miniaudio.h:6707
@ ma_backend_jack
Definition miniaudio.h:6715
MA_API void ma_pcm_f32_to_s32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref *pAudioBufferRef, void **ppFramesOut, ma_uint64 *pFrameCount)
ma_resource_manager_data_source_flags
Definition miniaudio.h:10296
@ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC
Definition miniaudio.h:10299
@ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH
Definition miniaudio.h:10301
@ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE
Definition miniaudio.h:10298
@ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM
Definition miniaudio.h:10297
@ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING
Definition miniaudio.h:10302
@ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT
Definition miniaudio.h:10300
MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref *pAudioBufferRef, ma_uint64 *pAvailableFrames)
MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener *pListener)
#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT
Definition miniaudio.h:5815
MA_API ma_uint32 ma_rb_available_write(ma_rb *pRB)
MA_API void ma_hpf1_uninit(ma_hpf1 *pHPF, const ma_allocation_callbacks *pAllocationCallbacks)
ma_uint32 ma_spinlock
Definition miniaudio.h:4425
MA_API ma_result ma_vfs_open(ma_vfs *pVFS, const char *pFilePath, ma_uint32 openMode, ma_vfs_file *pFile)
MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound *pSound, ma_uint64 absoluteGlobalTimeInFrames)
MA_API ma_result ma_resource_manager_next_job(ma_resource_manager *pResourceManager, ma_job *pJob)
MA_API void ma_pcm_u8_to_f32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound *pSound)
MA_API void ma_sound_set_pinned_listener_index(ma_sound *pSound, ma_uint32 listenerIndex)
MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb *pRB)
MA_API ma_node_state ma_node_get_state(const ma_node *pNode)
MA_API ma_result ma_slot_allocator_free(ma_slot_allocator *pAllocator, ma_uint64 slot)
MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2 *pFilter, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb *pRB)
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)
MA_API void ma_sound_set_looping(ma_sound *pSound, ma_bool32 isLooping)
MA_API ma_bool32 ma_sound_is_looping(const ma_sound *pSound)
MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude)
MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine *pEngine, ma_engine_node_type type, ma_uint32 flags)
MA_API void ma_channel_map_copy(ma_channel *pOut, const ma_channel *pIn, ma_uint32 channels)
MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound *pSound)
MA_API ma_result ma_engine_start(ma_engine *pEngine)
MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb *pRB)
MA_API ma_result ma_data_source_set_looping(ma_data_source *pDataSource, ma_bool32 isLooping)
MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener *pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source *pDataSource, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead)
MA_API void ma_spatializer_uninit(ma_spatializer *pSpatializer, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer *pAudioBuffer, ma_uint64 *pCursor)
MA_API void * ma_malloc(size_t sz, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config *pConfig, void *pHeap, ma_channel_converter *pConverter)
MA_API float ma_sound_get_volume(const ma_sound *pSound)
MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source *pDataSource, ma_uint64 *pCursor)
MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer *pSpatializer)
MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels)
Definition miniaudio.h:6148
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)
MA_API void ma_engine_listener_set_world_up(ma_engine *pEngine, ma_uint32 listenerIndex, float x, float y, float z)
MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad *pBQ, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock *pSpinlock)
MA_API void ma_node_uninit(ma_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
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)
MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb *pRB, ma_uint32 sizeInFrames)
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)
MA_API ma_result ma_panner_process_pcm_frames(ma_panner *pPanner, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
MA_API ma_result ma_engine_play_sound(ma_engine *pEngine, const char *pFilePath, ma_sound_group *pGroup)
MA_API void ma_clip_samples_s24(ma_uint8 *pDst, const ma_int64 *pSrc, ma_uint64 count)
MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config *pConfig, void *pHeap, ma_spatializer *pSpatializer)
#define MA_TRUE
Definition miniaudio.h:3823
MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound *pSound)
MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph *pNodeGraph)
MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager *pResourceManager, const wchar_t *pName)
MA_API ma_result ma_noise_set_type(ma_noise *pNoise, ma_noise_type type)
MA_API void ma_device_job_thread_uninit(ma_device_job_thread *pJobThread, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform *pWaveform, ma_uint64 frameIndex)
MA_API ma_result ma_data_converter_reset(ma_data_converter *pConverter)
MA_API ma_node * ma_engine_get_endpoint(ma_engine *pEngine)
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)
MA_API ma_result ma_node_get_heap_size(ma_node_graph *pNodeGraph, const ma_node_config *pConfig, size_t *pHeapSizeInBytes)
MA_API void ma_lpf2_uninit(ma_lpf2 *pLPF, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_result ma_gainer_set_gain(ma_gainer *pGainer, float newGain)
MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave *pWaveform, ma_uint32 sampleRate)
MA_API ma_result ma_lpf_init(const ma_lpf_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_lpf *pLPF)
MA_API ma_result ma_gainer_set_master_volume(ma_gainer *pGainer, float volume)
MA_API void ma_sound_set_positioning(ma_sound *pSound, ma_positioning positioning)
MA_API void ma_fader_set_fade(ma_fader *pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames)
ma_aaudio_usage
Definition miniaudio.h:6973
@ ma_aaudio_usage_emergency
Definition miniaudio.h:6987
@ ma_aaudio_usage_default
Definition miniaudio.h:6974
@ ma_aaudio_usage_voice_communication_signalling
Definition miniaudio.h:6977
@ ma_aaudio_usage_voice_communication
Definition miniaudio.h:6976
@ ma_aaudio_usage_assitant
Definition miniaudio.h:6986
@ ma_aaudio_usage_safety
Definition miniaudio.h:6988
@ ma_aaudio_usage_media
Definition miniaudio.h:6975
@ ma_aaudio_usage_notification
Definition miniaudio.h:6979
@ ma_aaudio_usage_announcement
Definition miniaudio.h:6990
@ ma_aaudio_usage_vehicle_status
Definition miniaudio.h:6989
@ ma_aaudio_usage_assistance_navigation_guidance
Definition miniaudio.h:6983
@ ma_aaudio_usage_assistance_sonification
Definition miniaudio.h:6984
@ ma_aaudio_usage_notification_event
Definition miniaudio.h:6981
@ ma_aaudio_usage_assistance_accessibility
Definition miniaudio.h:6982
@ ma_aaudio_usage_alarm
Definition miniaudio.h:6978
@ ma_aaudio_usage_game
Definition miniaudio.h:6985
@ ma_aaudio_usage_notification_ringtone
Definition miniaudio.h:6980
MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
MA_API ma_result ma_decoder_uninit(ma_decoder *pDecoder)
MA_API void * ma_calloc(size_t sz, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API float ma_engine_get_gain_db(ma_engine *pEngine)
MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config *pConfig, ma_paged_audio_buffer *pPagedAudioBuffer)
MA_API float ma_sound_group_get_max_gain(const ma_sound_group *pGroup)
MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node *pNode)
MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter *pConverter, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
MA_API float ma_fader_get_current_volume(const ma_fader *pFader)
MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb *pRB, ma_uint32 offsetInFrames)
ma_seek_origin
Definition miniaudio.h:9866
@ ma_seek_origin_end
Definition miniaudio.h:9869
@ ma_seek_origin_start
Definition miniaudio.h:9867
@ ma_seek_origin_current
Definition miniaudio.h:9868
MA_API float ma_delay_get_dry(const ma_delay *pDelay)
MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel *pChannelMap, ma_channel channelPosition)
MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb *pRB)
MA_API ma_result ma_waveform_set_sample_rate(ma_waveform *pWaveform, ma_uint32 sampleRate)
MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config *pConfig, ma_audio_buffer *pAudioBuffer)
MA_API void ma_sound_group_set_attenuation_model(ma_sound_group *pGroup, ma_attenuation_model attenuationModel)
MA_API ma_int32 ma_rb_pointer_distance(ma_rb *pRB)
MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer *pPagedAudioBuffer, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead)
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)
struct ma_resource_manager_data_stream ma_resource_manager_data_stream
Definition miniaudio.h:10292
MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group *pGroup, ma_uint64 absoluteGlobalTimeInFrames)
MA_API ma_result ma_event_signal(ma_event *pEvent)
MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels)
MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel *pChannelMap, ma_uint32 channels)
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)
MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group *pGroup)
MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad *pBQ)
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)
MA_API void ma_spatializer_set_position(ma_spatializer *pSpatializer, float x, float y, float z)
MA_API ma_result ma_lpf_reinit(const ma_lpf_config *pConfig, ma_lpf *pLPF)
MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf *pBPF, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
MA_API void ma_channel_converter_uninit(ma_channel_converter *pConverter, const ma_allocation_callbacks *pAllocationCallbacks)
ma_resource_manager_flags
Definition miniaudio.h:10352
@ MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING
Definition miniaudio.h:10354
@ MA_RESOURCE_MANAGER_FLAG_NO_THREADING
Definition miniaudio.h:10357
MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
MA_API void ma_spatializer_set_velocity(ma_spatializer *pSpatializer, float x, float y, float z)
ma_aaudio_input_preset
Definition miniaudio.h:7005
@ ma_aaudio_input_preset_voice_communication
Definition miniaudio.h:7010
@ ma_aaudio_input_preset_default
Definition miniaudio.h:7006
@ ma_aaudio_input_preset_camcorder
Definition miniaudio.h:7008
@ ma_aaudio_input_preset_voice_performance
Definition miniaudio.h:7012
@ ma_aaudio_input_preset_unprocessed
Definition miniaudio.h:7011
@ ma_aaudio_input_preset_generic
Definition miniaudio.h:7007
@ ma_aaudio_input_preset_voice_recognition
Definition miniaudio.h:7009
MA_API void ma_pcm_s24_to_u8(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager *pResourceManager, const char *pName, const void *pData, size_t sizeInBytes)
MA_API float ma_sound_group_get_volume(const ma_sound_group *pGroup)
MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend)
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)
MA_API ma_result ma_get_backend_from_name(const char *pBackendName, ma_backend *pBackend)
signed int ma_int32
Definition miniaudio.h:3794
MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group *pGroup)
MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb *pRB, ma_uint32 *pSizeInFrames, void **ppBufferOut)
MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph *pNodeGraph, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead)
MA_API void ma_sound_group_set_cone(ma_sound_group *pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
ma_sound_config ma_sound_group_config
Definition miniaudio.h:11247
MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount)
MA_API void ma_delay_uninit(ma_delay *pDelay, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API void ma_sound_group_set_max_gain(ma_sound_group *pGroup, float maxGain)
MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine *pEngine, ma_uint64 globalTime)
MA_API void ma_spatializer_set_doppler_factor(ma_spatializer *pSpatializer, float dopplerFactor)
MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager *pResourceManager, const wchar_t *pFilePath)
MA_API ma_result ma_waveform_init(const ma_waveform_config *pConfig, ma_waveform *pWaveform)
MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter *pConverter, ma_channel *pChannelMap, size_t channelMapCap)
MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config *pConfig, ma_lpf_node *pNode)
ma_result(* ma_decoder_seek_proc)(ma_decoder *pDecoder, ma_int64 byteOffset, ma_seek_origin origin)
Definition miniaudio.h:9959
MA_API ma_result ma_data_source_seek_seconds(ma_data_source *pDataSource, float secondCount, float *pSecondsSeeked)
MA_API ma_result ma_resampler_set_rate(ma_resampler *pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
MA_API void ma_sound_group_set_min_gain(ma_sound_group *pGroup, float minGain)
MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8 *pSamplesOut, const ma_uint8 *pSamplesIn, ma_uint64 sampleCount, float factor)
MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
MA_API ma_result ma_node_set_state(ma_node *pNode, ma_node_state state)
MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler *pResampler, ma_uint64 outputFrameCount, ma_uint64 *pInputFrameCount)
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)
#define MA_SIZE_MAX
Definition miniaudio.h:3857
#define MA_ATOMIC_SAFE_TYPE_DECL(c89TypeExtension, typeSize, type)
Definition miniaudio.h:4405
struct ma_device ma_device
Definition miniaudio.h:4109
MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager *pResourceManager)
MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
MA_API void ma_apply_volume_factor_u8(ma_uint8 *pSamples, ma_uint64 sampleCount, float factor)
MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void *pInterleavedPCMFrames, void **ppDeinterleavedPCMFrames)
MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data *pData, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API float ma_delay_node_get_decay(const ma_delay_node *pDelayNode)
MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler *pResampler, ma_uint64 inputFrameCount, ma_uint64 *pOutputFrameCount)
MA_API ma_result ma_decode_from_vfs(ma_vfs *pVFS, const char *pFilePath, ma_decoder_config *pConfig, ma_uint64 *pFrameCountOut, void **ppPCMFramesOut)
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)
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)
MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb *pRB, ma_uint32 offsetInFrames)
struct ma_node_output_bus ma_node_output_bus
Definition miniaudio.h:10707
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)
void(* ma_device_data_proc)(ma_device *pDevice, void *pOutput, const void *pInput, ma_uint32 frameCount)
Definition miniaudio.h:6875
MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener *pListener)
MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler *pResampler, ma_uint64 outputFrameCount, ma_uint64 *pInputFrameCount)
MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config *pConfig, size_t *pHeapSizeInBytes)
MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node *pNode)
MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2 *pFilter, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source *pDataSource)
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)
ma_wasapi_usage
Definition miniaudio.h:6965
@ ma_wasapi_usage_games
Definition miniaudio.h:6967
@ ma_wasapi_usage_pro_audio
Definition miniaudio.h:6968
@ ma_wasapi_usage_default
Definition miniaudio.h:6966
MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data *pData, ma_paged_audio_buffer_page *pPage)
#define MA_FALSE
Definition miniaudio.h:3824
MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter *pConverter, ma_channel *pChannelMap, size_t channelMapCap)
MA_API void ma_lpf_uninit(ma_lpf *pLPF, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API void ma_hishelf_node_uninit(ma_hishelf_node *pNode, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream *pDataStream, ma_uint64 *pAvailableFrames)
struct ma_node_input_bus ma_node_input_bus
Definition miniaudio.h:10731
MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer *pSpatializer)
MA_API ma_result ma_sound_group_init(ma_engine *pEngine, ma_uint32 flags, ma_sound_group *pParentGroup, ma_sound_group *pGroup)
MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16 *pSamplesOut, const ma_int16 *pSamplesIn, ma_uint64 sampleCount, float factor)
MA_API void ma_hpf2_uninit(ma_hpf2 *pHPF, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_result ma_delay_init(const ma_delay_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_delay *pDelay)
MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler *pResampler)
MA_API void ma_slot_allocator_uninit(ma_slot_allocator *pAllocator, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config *pConfig, void *pHeap, ma_hpf1 *pLPF)
MA_API ma_bool32 ma_sound_at_end(const ma_sound *pSound)
MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event *pNotificationEvent)
struct ma_device_config ma_device_config
Definition miniaudio.h:7057
MA_API void ma_resampler_uninit(ma_resampler *pResampler, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API void ma_sound_set_directional_attenuation_factor(ma_sound *pSound, float directionalAttenuationFactor)
MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound *pSound, ma_uint64 *pLength)
MA_API void ma_pcm_s32_to_s16(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
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)
MA_API void ma_pcm_f32_to_u8(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_opensl_recording_preset
Definition miniaudio.h:6954
@ ma_opensl_recording_preset_default
Definition miniaudio.h:6955
@ ma_opensl_recording_preset_voice_communication
Definition miniaudio.h:6959
@ ma_opensl_recording_preset_generic
Definition miniaudio.h:6956
@ ma_opensl_recording_preset_voice_unprocessed
Definition miniaudio.h:6960
@ ma_opensl_recording_preset_voice_recognition
Definition miniaudio.h:6958
@ ma_opensl_recording_preset_camcorder
Definition miniaudio.h:6957
ma_standard_channel_map
Definition miniaudio.h:4334
@ ma_standard_channel_map_default
Definition miniaudio.h:4343
@ ma_standard_channel_map_vorbis
Definition miniaudio.h:4339
@ ma_standard_channel_map_sound4
Definition miniaudio.h:4340
@ ma_standard_channel_map_sndio
Definition miniaudio.h:4341
@ ma_standard_channel_map_webaudio
Definition miniaudio.h:4342
@ ma_standard_channel_map_flac
Definition miniaudio.h:4338
@ ma_standard_channel_map_alsa
Definition miniaudio.h:4336
@ ma_standard_channel_map_rfc3551
Definition miniaudio.h:4337
@ ma_standard_channel_map_microsoft
Definition miniaudio.h:4335
struct ma_resampler_config ma_resampler_config
Definition miniaudio.h:5361
MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config *pConfig, ma_bpf2 *pBPF)
ma_job_type
Definition miniaudio.h:6376
@ MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM
Definition miniaudio.h:6390
@ MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM
Definition miniaudio.h:6389
@ MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER
Definition miniaudio.h:6385
@ MA_JOB_TYPE_QUIT
Definition miniaudio.h:6378
@ MA_JOB_TYPE_CUSTOM
Definition miniaudio.h:6379
@ MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER
Definition miniaudio.h:6386
@ MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM
Definition miniaudio.h:6388
@ MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE
Definition miniaudio.h:6382
@ MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM
Definition miniaudio.h:6387
@ MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE
Definition miniaudio.h:6384
@ MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE
Definition miniaudio.h:6393
@ MA_JOB_TYPE_COUNT
Definition miniaudio.h:6396
@ MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE
Definition miniaudio.h:6383
MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound *pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds)
MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group *pGroup)
MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source *pDataSource)
MA_API ma_result ma_node_detach_all_output_buses(ma_node *pNode)
MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
MA_API void ma_apply_volume_factor_pcm_frames_f32(float *pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
MA_API float ma_delay_get_wet(const ma_delay *pDelay)
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)
MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32 *pSamplesOut, const ma_int32 *pSamplesIn, ma_uint64 sampleCount, float factor)
MA_API ma_result ma_device_get_name(ma_device *pDevice, ma_device_type type, char *pName, size_t nameCap, size_t *pLengthNotIncludingNullTerminator)
MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node *pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd)
MA_API void ma_fader_get_data_format(const ma_fader *pFader, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate)
MA_API void ma_pcm_f32_to_s24(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API void ma_sound_group_set_min_distance(ma_sound_group *pGroup, float minDistance)
MA_API void ma_log_uninit(ma_log *pLog)
MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config *pConfig, size_t *pHeapSizeInBytes)
MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config *pConfig, void *pHeap, ma_notch2 *pFilter)
MA_API ma_result ma_node_init(ma_node_graph *pNodeGraph, const ma_node_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_node *pNode)
struct ma_resource_manager_data_source ma_resource_manager_data_source
Definition miniaudio.h:10293
MA_API ma_result ma_biquad_init(const ma_biquad_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_biquad *pBQ)
MA_API float ma_spatializer_get_min_distance(const ma_spatializer *pSpatializer)
MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream *pDataStream)
struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page
Definition miniaudio.h:5948
MA_API void ma_rb_uninit(ma_rb *pRB)
MA_API void ma_sound_set_max_gain(ma_sound *pSound, float maxGain)
MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave *pWaveform, double amplitude)
ma_device_state
Definition miniaudio.h:6682
@ ma_device_state_uninitialized
Definition miniaudio.h:6683
@ ma_device_state_stopped
Definition miniaudio.h:6684
@ ma_device_state_starting
Definition miniaudio.h:6686
@ ma_device_state_stopping
Definition miniaudio.h:6687
@ ma_device_state_started
Definition miniaudio.h:6685
MA_API void ma_rb_reset(ma_rb *pRB)
MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref *pAudioBufferRef, const void *pData, ma_uint64 sizeInFrames)
MA_API void ma_engine_listener_set_velocity(ma_engine *pEngine, ma_uint32 listenerIndex, float x, float y, float z)
MA_API ma_result ma_engine_set_volume(ma_engine *pEngine, float volume)
MA_API ma_channel ma_channel_map_get_channel(const ma_channel *pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)
void ma_data_source
Definition miniaudio.h:5813
MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config *pConfig, ma_hpf1 *pHPF)
MA_API ma_result ma_device_init(ma_context *pContext, const ma_device_config *pConfig, ma_device *pDevice)
MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler *pResampler, float ratio)
MA_API ma_job ma_job_init(ma_uint16 code)
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)
MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void **ppDeinterleavedPCMFrames, void *pInterleavedPCMFrames)
MA_API float ma_sound_get_rolloff(const ma_sound *pSound)
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)
MA_API ma_sound_config ma_sound_config_init(void)
MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel *pChannelMap, size_t channelMapCap, ma_uint32 channels)
ma_bool32(* ma_enum_devices_callback_proc)(ma_context *pContext, ma_device_type deviceType, const ma_device_info *pInfo, void *pUserData)
Definition miniaudio.h:7187
ma_node_flags
Definition miniaudio.h:10630
@ MA_NODE_FLAG_SILENT_OUTPUT
Definition miniaudio.h:10635
@ MA_NODE_FLAG_CONTINUOUS_PROCESSING
Definition miniaudio.h:10632
@ MA_NODE_FLAG_PASSTHROUGH
Definition miniaudio.h:10631
@ MA_NODE_FLAG_ALLOW_NULL_INPUT
Definition miniaudio.h:10633
@ MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES
Definition miniaudio.h:10634
MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source *pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames)
#define MA_UINT64_MAX
Definition miniaudio.h:3860
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)
MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source *pDataSource)
MA_API void ma_pcm_f32_to_s16(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config *pConfig, size_t *pHeapSizeInBytes)
MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound *pSound)
MA_API ma_uint32 ma_node_get_input_channels(const ma_node *pNode, ma_uint32 inputBusIndex)
MA_API ma_result ma_device_job_thread_next(ma_device_job_thread *pJobThread, ma_job *pJob)
struct ma_sound ma_sound
Definition miniaudio.h:11094
struct ma_sound_inlined ma_sound_inlined
Definition miniaudio.h:11238
MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder *pDecoder, ma_uint64 *pLength)
MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config *pConfig, ma_pulsewave *pWaveform)
MA_API void ma_pcm_rb_uninit(ma_pcm_rb *pRB)
MA_API void ma_sound_uninit(ma_sound *pSound)
ma_opensl_stream_type
Definition miniaudio.h:6942
@ ma_opensl_stream_type_media
Definition miniaudio.h:6947
@ ma_opensl_stream_type_ring
Definition miniaudio.h:6946
@ ma_opensl_stream_type_alarm
Definition miniaudio.h:6948
@ ma_opensl_stream_type_notification
Definition miniaudio.h:6949
@ ma_opensl_stream_type_voice
Definition miniaudio.h:6944
@ ma_opensl_stream_type_default
Definition miniaudio.h:6943
@ ma_opensl_stream_type_system
Definition miniaudio.h:6945
MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void *pUserData)
MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config *pConfig, void *pHeap, ma_lpf *pLPF)
MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config *pConfig, size_t *pHeapSizeInBytes)
MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore *pSemaphore)
void(* ma_sound_end_proc)(void *pUserData, ma_sound *pSound)
Definition miniaudio.h:11187
MA_API ma_result ma_peak2_init(const ma_peak2_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_peak2 *pFilter)
MA_API void ma_gainer_uninit(ma_gainer *pGainer, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config *pConfig, ma_loshelf2 *pFilter)
MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer *pPagedAudioBuffer, ma_uint64 frameIndex)
MA_API size_t ma_rb_get_subbuffer_stride(ma_rb *pRB)
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)
MA_API ma_result ma_device_start(ma_device *pDevice)
MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine *pEngine, ma_uint32 listenerIndex)
MA_API void ma_sound_set_attenuation_model(ma_sound *pSound, ma_attenuation_model attenuationModel)
MA_API ma_channel * ma_spatializer_listener_get_channel_map(ma_spatializer_listener *pListener)
MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer *pAudioBuffer, ma_uint64 *pAvailableFrames)
MA_API void ma_data_source_node_uninit(ma_data_source_node *pDataSourceNode, const ma_allocation_callbacks *pAllocationCallbacks)
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)
MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener *pListener, float x, float y, float z)
MA_API ma_pan_mode ma_panner_get_mode(const ma_panner *pPanner)
MA_API void ma_clip_samples_u8(ma_uint8 *pDst, const ma_int16 *pSrc, ma_uint64 count)
ma_pthread_mutex_t ma_mutex
Definition miniaudio.h:4448
MA_API void ma_bpf_uninit(ma_bpf *pBPF, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API void * ma_offset_pcm_frames_ptr(void *p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event *pNotificationEvent)
MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency)
MA_API ma_vec3f ma_sound_get_velocity(const ma_sound *pSound)
MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data *pData)
MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source *pDataSource, ma_uint64 *pCursor)
ma_stream_layout
Definition miniaudio.h:4270
@ ma_stream_layout_deinterleaved
Definition miniaudio.h:4272
@ ma_stream_layout_interleaved
Definition miniaudio.h:4271
MA_API void ma_sound_set_pitch(ma_sound *pSound, float pitch)
struct ma_decoder ma_decoder
Definition miniaudio.h:9936
MA_API ma_result ma_waveform_set_amplitude(ma_waveform *pWaveform, double amplitude)
ma_pthread_t ma_thread
Definition miniaudio.h:4442
MA_API void ma_pcm_u8_to_s32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API ma_result ma_biquad_clear_cache(ma_biquad *pBQ)
#define MA_NO_INLINE
Definition miniaudio.h:3987
MA_API void ma_linear_resampler_uninit(ma_linear_resampler *pResampler, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config *pConfig, void *pHeap, ma_gainer *pGainer)
MA_API void ma_spatializer_set_rolloff(ma_spatializer *pSpatializer, float rolloff)
#define MA_VERSION_REVISION
Definition miniaudio.h:3753
MA_API void ma_spatializer_set_max_gain(ma_spatializer *pSpatializer, float maxGain)
MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group *pGroup, float directionalAttenuationFactor)
MA_API ma_result ma_noise_set_amplitude(ma_noise *pNoise, double amplitude)
MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data *pData, ma_uint64 *pLength)
MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1 *pLPF, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float *pDst, const float *pSrc, ma_uint64 count, float volume)
MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source *pDataSource, ma_uint64 *pRangeBegInFrames, ma_uint64 *pRangeEndInFrames)
void(* ma_engine_process_proc)(void *pUserData, float *pFramesOut, ma_uint64 frameCount)
Definition miniaudio.h:11253
MA_API void ma_lpf1_uninit(ma_lpf1 *pLPF, const ma_allocation_callbacks *pAllocationCallbacks)
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)
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)
MA_API ma_result ma_resampler_init(const ma_resampler_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_resampler *pResampler)
MA_API float ma_spatializer_get_min_gain(const ma_spatializer *pSpatializer)
MA_API void ma_sound_group_set_position(ma_sound_group *pGroup, float x, float y, float z)
MA_API void ma_sound_group_set_pan(ma_sound_group *pGroup, float pan)
MA_API void ma_copy_and_apply_volume_factor_s24(void *pSamplesOut, const void *pSamplesIn, ma_uint64 sampleCount, float factor)
MA_API ma_result ma_sound_group_start(ma_sound_group *pGroup)
MA_API void ma_engine_listener_set_position(ma_engine *pEngine, ma_uint32 listenerIndex, float x, float y, float z)
MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine *pEngine)
MA_API ma_result ma_data_source_set_current(ma_data_source *pDataSource, ma_data_source *pCurrentDataSource)
MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine *pEngine, ma_uint64 globalTime)
MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener *pListener)
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)
MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine *pEngine)
MA_API ma_result ma_decoder_init_file(const char *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
MA_API ma_paged_audio_buffer_page * ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data *pData)
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)
MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config *pConfig, ma_audio_buffer **ppAudioBuffer)
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)
MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream *pDataStream)
MA_API ma_result ma_node_attach_output_bus(ma_node *pNode, ma_uint32 outputBusIndex, ma_node *pOtherNode, ma_uint32 otherNodeInputBusIndex)
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)
MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb *pRB, ma_uint32 subbufferIndex)
MA_API void ma_engine_uninit(ma_engine *pEngine)
MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler *pResampler)
MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel *pChannelMapA, const ma_channel *pChannelMapB, ma_uint32 channels)
MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer *pSpatializer)
MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream *pDataStream, ma_uint64 *pCursor)
MA_API ma_result ma_device_job_thread_post(ma_device_job_thread *pJobThread, const ma_job *pJob)
ma_attenuation_model
Definition miniaudio.h:5130
@ ma_attenuation_model_inverse
Definition miniaudio.h:5132
@ ma_attenuation_model_none
Definition miniaudio.h:5131
@ ma_attenuation_model_linear
Definition miniaudio.h:5133
@ ma_attenuation_model_exponential
Definition miniaudio.h:5134
MA_API void ma_spatializer_set_cone(ma_spatializer *pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
MA_API void ma_delay_set_dry(ma_delay *pDelay, float value)
MA_API void ma_sound_group_set_direction(ma_sound_group *pGroup, float x, float y, float z)
MA_API ma_result ma_decode_memory(const void *pData, size_t dataSize, ma_decoder_config *pConfig, ma_uint64 *pFrameCountOut, void **ppPCMFramesOut)
#define MA_NODE_BUS_COUNT_UNKNOWN
Definition miniaudio.h:10612
#define MA_MAX_CHANNELS
Definition miniaudio.h:4257
MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer *pSpatializer)
signed long long ma_int64
Definition miniaudio.h:3807
MA_API void ma_sound_set_min_distance(ma_sound *pSound, float minDistance)
MA_API void ma_sound_set_pan(ma_sound *pSound, float pan)
MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source *pDataSource, ma_uint64 *pLength)
MA_API ma_result ma_semaphore_release(ma_semaphore *pSemaphore)
MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config *pConfig, void *pHeap, ma_peak2 *pFilter)
MA_API ma_data_source * ma_sound_get_data_source(const ma_sound *pSound)
MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config *pConfig, size_t *pHeapSizeInBytes)
MA_API void ma_splitter_node_uninit(ma_splitter_node *pSplitterNode, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2 *pFilter)
MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group *pGroup, ma_bool32 enabled)
MA_API void ma_pcm_s16_to_s24(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity)
MA_API size_t ma_rb_get_subbuffer_size(ma_rb *pRB)
MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group *pGroup)
struct ma_encoder ma_encoder
Definition miniaudio.h:10101
void ma_vfs
Definition miniaudio.h:9856
MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb *pRB)
MA_API ma_result ma_waveform_set_frequency(ma_waveform *pWaveform, double frequency)
MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2 *pFilter)
MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)
MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream *pDataStream, ma_uint64 frameIndex)
MA_API ma_uint32 ma_rb_available_read(ma_rb *pRB)
MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group *pGroup)
ma_noise_type
Definition miniaudio.h:10222
@ ma_noise_type_brownian
Definition miniaudio.h:10225
@ ma_noise_type_white
Definition miniaudio.h:10223
@ ma_noise_type_pink
Definition miniaudio.h:10224
ma_result(* ma_encoder_init_proc)(ma_encoder *pEncoder)
Definition miniaudio.h:10105
MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb *pRB, ma_uint32 sizeInFrames)
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)
MA_API ma_uint32 ma_engine_get_channels(const ma_engine *pEngine)
MA_API ma_result ma_job_queue_init(const ma_job_queue_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_job_queue *pQueue)
MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config *pConfig, size_t *pHeapSizeInBytes)
MA_API ma_result ma_lpf2_clear_cache(ma_lpf2 *pLPF)
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)
ma_result
Definition miniaudio.h:4172
@ MA_NO_DATA_AVAILABLE
Definition miniaudio.h:4205
@ MA_IS_DIRECTORY
Definition miniaudio.h:4188
@ MA_NO_ADDRESS
Definition miniaudio.h:4211
@ MA_FAILED_TO_STOP_BACKEND_DEVICE
Definition miniaudio.h:4251
@ MA_MEMORY_ALREADY_MAPPED
Definition miniaudio.h:4225
@ MA_DOES_NOT_EXIST
Definition miniaudio.h:4180
@ MA_PATH_TOO_LONG
Definition miniaudio.h:4185
@ MA_PROTOCOL_UNAVAILABLE
Definition miniaudio.h:4213
@ MA_API_NOT_FOUND
Definition miniaudio.h:4236
@ MA_NOT_IMPLEMENTED
Definition miniaudio.h:4202
@ MA_NO_NETWORK
Definition miniaudio.h:4208
@ MA_TIMEOUT
Definition miniaudio.h:4207
@ MA_ALREADY_EXISTS
Definition miniaudio.h:4181
@ MA_IO_ERROR
Definition miniaudio.h:4193
@ MA_FAILED_TO_OPEN_BACKEND_DEVICE
Definition miniaudio.h:4249
@ MA_BAD_MESSAGE
Definition miniaudio.h:4204
@ MA_ERROR
Definition miniaudio.h:4174
@ MA_OUT_OF_RANGE
Definition miniaudio.h:4178
@ MA_CANCELLED
Definition miniaudio.h:4224
@ MA_DEVICE_NOT_STOPPED
Definition miniaudio.h:4245
@ MA_BAD_SEEK
Definition miniaudio.h:4198
@ MA_ALREADY_IN_USE
Definition miniaudio.h:4196
@ MA_INVALID_ARGS
Definition miniaudio.h:4175
@ MA_FAILED_TO_START_BACKEND_DEVICE
Definition miniaudio.h:4250
@ MA_ADDRESS_FAMILY_NOT_SUPPORTED
Definition miniaudio.h:4216
@ MA_INVALID_FILE
Definition miniaudio.h:4183
@ MA_DEADLOCK
Definition miniaudio.h:4200
@ MA_NO_BACKEND
Definition miniaudio.h:4234
@ MA_DEVICE_TYPE_NOT_SUPPORTED
Definition miniaudio.h:4232
@ MA_BAD_PROTOCOL
Definition miniaudio.h:4212
@ MA_BACKEND_NOT_ENABLED
Definition miniaudio.h:4239
@ MA_PROTOCOL_NOT_SUPPORTED
Definition miniaudio.h:4214
@ MA_NOT_CONNECTED
Definition miniaudio.h:4220
@ MA_INVALID_OPERATION
Definition miniaudio.h:4176
@ MA_DEVICE_NOT_INITIALIZED
Definition miniaudio.h:4242
@ MA_NOT_SOCKET
Definition miniaudio.h:4210
@ MA_BUSY
Definition miniaudio.h:4192
@ MA_TOO_MANY_OPEN_FILES
Definition miniaudio.h:4182
@ MA_PROTOCOL_FAMILY_NOT_SUPPORTED
Definition miniaudio.h:4215
@ MA_FAILED_TO_INIT_BACKEND
Definition miniaudio.h:4248
@ MA_NOT_DIRECTORY
Definition miniaudio.h:4187
@ MA_NO_HOST
Definition miniaudio.h:4222
@ MA_NOT_UNIQUE
Definition miniaudio.h:4209
@ MA_CONNECTION_RESET
Definition miniaudio.h:4218
@ MA_LOOP
Definition miniaudio.h:4238
@ MA_BAD_PIPE
Definition miniaudio.h:4199
@ MA_INTERRUPT
Definition miniaudio.h:4194
@ MA_ALREADY_CONNECTED
Definition miniaudio.h:4219
@ MA_DEVICE_NOT_STARTED
Definition miniaudio.h:4244
@ MA_TOO_BIG
Definition miniaudio.h:4184
@ MA_ACCESS_DENIED
Definition miniaudio.h:4179
@ MA_AT_END
Definition miniaudio.h:4190
@ MA_UNAVAILABLE
Definition miniaudio.h:4195
@ MA_IN_PROGRESS
Definition miniaudio.h:4223
@ MA_INVALID_DATA
Definition miniaudio.h:4206
@ MA_OUT_OF_MEMORY
Definition miniaudio.h:4177
@ MA_BAD_ADDRESS
Definition miniaudio.h:4197
@ MA_CONNECTION_REFUSED
Definition miniaudio.h:4221
@ MA_DIRECTORY_NOT_EMPTY
Definition miniaudio.h:4189
@ MA_DEVICE_ALREADY_INITIALIZED
Definition miniaudio.h:4243
@ MA_NO_SPACE
Definition miniaudio.h:4191
@ MA_NAME_TOO_LONG
Definition miniaudio.h:4186
@ MA_TOO_MANY_LINKS
Definition miniaudio.h:4201
@ MA_NO_DEVICE
Definition miniaudio.h:4235
@ MA_SHARE_MODE_NOT_SUPPORTED
Definition miniaudio.h:4233
@ MA_CRC_MISMATCH
Definition miniaudio.h:4228
@ MA_INVALID_DEVICE_CONFIG
Definition miniaudio.h:4237
@ MA_NO_MESSAGE
Definition miniaudio.h:4203
@ MA_SOCKET_NOT_SUPPORTED
Definition miniaudio.h:4217
@ MA_FORMAT_NOT_SUPPORTED
Definition miniaudio.h:4231
@ MA_SUCCESS
Definition miniaudio.h:4173
ma_stream_format
Definition miniaudio.h:4265
@ ma_stream_format_pcm
Definition miniaudio.h:4266
MA_API void ma_delay_set_decay(ma_delay *pDelay, float value)
MA_API void ma_sound_set_position(ma_sound *pSound, float x, float y, float z)
MA_API void ma_panner_set_mode(ma_panner *pPanner, ma_pan_mode mode)
MA_API ma_result ma_node_graph_init(const ma_node_graph_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_node_graph *pNodeGraph)
MA_API void ma_sound_group_set_positioning(ma_sound_group *pGroup, ma_positioning positioning)
#define MA_MAX_LOG_CALLBACKS
Definition miniaudio.h:4510
ma_encoding_format
Definition miniaudio.h:9917
@ ma_encoding_format_flac
Definition miniaudio.h:9920
@ ma_encoding_format_mp3
Definition miniaudio.h:9921
@ ma_encoding_format_vorbis
Definition miniaudio.h:9922
@ ma_encoding_format_unknown
Definition miniaudio.h:9918
@ ma_encoding_format_wav
Definition miniaudio.h:9919
MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group *pGroup)
MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source *pDataSource, ma_uint64 *pAvailableFrames)
MA_API ma_result ma_decode_file(const char *pFilePath, ma_decoder_config *pConfig, ma_uint64 *pFrameCountOut, void **ppPCMFramesOut)
ma_resource_manager_data_supply_type
Definition miniaudio.h:10378
@ ma_resource_manager_data_supply_type_decoded
Definition miniaudio.h:10381
@ ma_resource_manager_data_supply_type_unknown
Definition miniaudio.h:10379
@ ma_resource_manager_data_supply_type_encoded
Definition miniaudio.h:10380
@ ma_resource_manager_data_supply_type_decoded_paged
Definition miniaudio.h:10382
MA_API void ma_engine_listener_set_enabled(ma_engine *pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled)
MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config *pConfig, void *pHeap, ma_hishelf2 *pFilter)
MA_API ma_result ma_node_init_preallocated(ma_node_graph *pNodeGraph, const ma_node_config *pConfig, void *pHeap, ma_node *pNode)
MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound *pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)
MA_API ma_data_source_config ma_data_source_config_init(void)
MA_API float ma_sound_get_pan(const ma_sound *pSound)
MA_API ma_result ma_semaphore_wait(ma_semaphore *pSemaphore)
MA_API ma_result ma_engine_set_gain_db(ma_engine *pEngine, float gainDB)
MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound *pSound, ma_uint64 frameIndex)
ma_format
Definition miniaudio.h:4283
@ ma_format_s24
Definition miniaudio.h:4291
@ ma_format_count
Definition miniaudio.h:4294
@ ma_format_f32
Definition miniaudio.h:4293
@ ma_format_s16
Definition miniaudio.h:4290
@ ma_format_u8
Definition miniaudio.h:4289
@ ma_format_unknown
Definition miniaudio.h:4288
@ ma_format_s32
Definition miniaudio.h:4292
MA_API float ma_sound_get_current_fade_volume(const ma_sound *pSound)
MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group *pGroup)
MA_API ma_result ma_device_set_master_volume(ma_device *pDevice, float volume)
MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config *pConfig, ma_hpf2 *pHPF)
MA_API size_t ma_context_sizeof(void)
MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer *pPagedAudioBuffer, ma_uint64 *pCursor)
ma_data_converter_execution_path
Definition miniaudio.h:5596
@ ma_data_converter_execution_path_channels_first
Definition miniaudio.h:5602
@ ma_data_converter_execution_path_resample_first
Definition miniaudio.h:5601
@ ma_data_converter_execution_path_resample_only
Definition miniaudio.h:5600
@ ma_data_converter_execution_path_passthrough
Definition miniaudio.h:5597
@ ma_data_converter_execution_path_format_only
Definition miniaudio.h:5598
@ ma_data_converter_execution_path_channels_only
Definition miniaudio.h:5599
MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound *pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)
MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler *pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
MA_API float ma_spatializer_get_rolloff(const ma_spatializer *pSpatializer)
MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group *pGroup)
MA_API ma_result ma_device_set_master_volume_db(ma_device *pDevice, float gainDB)
MA_API void ma_noise_uninit(ma_noise *pNoise, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_result ma_noise_init_preallocated(const ma_noise_config *pConfig, void *pHeap, ma_noise *pNoise)
MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2 *pBPF, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
ma_open_mode_flags
Definition miniaudio.h:9860
@ MA_OPEN_MODE_WRITE
Definition miniaudio.h:9862
@ MA_OPEN_MODE_READ
Definition miniaudio.h:9861
MA_API float ma_sound_group_get_min_gain(const ma_sound_group *pGroup)
MA_API void ma_version(ma_uint32 *pMajor, ma_uint32 *pMinor, ma_uint32 *pRevision)
ma_sound_flags
Definition miniaudio.h:11099
@ MA_SOUND_FLAG_STREAM
Definition miniaudio.h:11101
@ MA_SOUND_FLAG_LOOPING
Definition miniaudio.h:11106
@ MA_SOUND_FLAG_WAIT_INIT
Definition miniaudio.h:11104
@ MA_SOUND_FLAG_NO_PITCH
Definition miniaudio.h:11110
@ MA_SOUND_FLAG_ASYNC
Definition miniaudio.h:11103
@ MA_SOUND_FLAG_UNKNOWN_LENGTH
Definition miniaudio.h:11105
@ MA_SOUND_FLAG_DECODE
Definition miniaudio.h:11102
@ MA_SOUND_FLAG_NO_SPATIALIZATION
Definition miniaudio.h:11111
@ MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT
Definition miniaudio.h:11109
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)
MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_spatializer_listener *pListener)
MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer *pSpatializer, float *pVolume)
MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8 *pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine *pEngine, float absolutePosX, float absolutePosY, float absolutePosZ)
MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_linear_resampler *pResampler)
MA_API void ma_semaphore_uninit(ma_semaphore *pSemaphore)
MA_API void ma_channel_map_init_blank(ma_channel *pChannelMap, ma_uint32 channels)
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)
MA_API ma_result ma_bpf_reinit(const ma_bpf_config *pConfig, ma_bpf *pBPF)
MA_API float ma_node_get_output_bus_volume(const ma_node *pNode, ma_uint32 outputBusIndex)
MA_API size_t ma_rb_get_subbuffer_offset(ma_rb *pRB, size_t subbufferIndex)
ma_aaudio_allowed_capture_policy
Definition miniaudio.h:7016
@ ma_aaudio_allow_capture_default
Definition miniaudio.h:7017
@ ma_aaudio_allow_capture_by_all
Definition miniaudio.h:7018
@ ma_aaudio_allow_capture_by_system
Definition miniaudio.h:7019
@ ma_aaudio_allow_capture_by_none
Definition miniaudio.h:7020
MA_API void * ma_rb_get_subbuffer_ptr(ma_rb *pRB, size_t subbufferIndex, void *pBuffer)
#define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT
Definition miniaudio.h:10348
MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer *pDataBuffer, ma_uint64 *pAvailableFrames)
MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref *pAudioBufferRef, ma_uint64 frameIndex)
static MA_INLINE const float * ma_offset_pcm_frames_const_ptr_f32(const float *p, ma_uint64 offsetInFrames, ma_uint32 channels)
Definition miniaudio.h:9771
ma_device_notification_type
Definition miniaudio.h:6758
@ ma_device_notification_type_started
Definition miniaudio.h:6759
@ ma_device_notification_type_stopped
Definition miniaudio.h:6760
@ ma_device_notification_type_interruption_ended
Definition miniaudio.h:6763
@ ma_device_notification_type_rerouted
Definition miniaudio.h:6761
@ ma_device_notification_type_interruption_began
Definition miniaudio.h:6762
@ ma_device_notification_type_unlocked
Definition miniaudio.h:6764
MA_API float ma_sound_group_get_rolloff(const ma_sound_group *pGroup)
MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config *pConfig, void *pHeap, ma_data_converter *pConverter)
signed short ma_int16
Definition miniaudio.h:3792
MA_API ma_result ma_delay_process_pcm_frames(ma_delay *pDelay, void *pFramesOut, const void *pFramesIn, ma_uint32 frameCount)
MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config *pConfig, ma_loshelf_node *pNode)
MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut)
MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer *pSpatializer, float directionalAttenuationFactor)
MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder *pDecoder, ma_uint64 frameIndex)
MA_API float ma_spatializer_get_max_distance(const ma_spatializer *pSpatializer)
MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound *pSound, float *pCursor)
#define MA_VERSION_MAJOR
Definition miniaudio.h:3751
MA_API ma_sound_group_config ma_sound_group_config_init(void)
MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config *pConfig, size_t *pHeapSizeInBytes)
MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine *pEngine)
MA_API void ma_sound_group_uninit(ma_sound_group *pGroup)
MA_API void ma_sound_group_set_pitch(ma_sound_group *pGroup, float pitch)
void(* ma_stop_proc)(ma_device *pDevice)
Definition miniaudio.h:6899
MA_API void ma_pcm_s24_to_s16(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API void ma_sound_set_cone(ma_sound *pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter *pConverter, ma_channel *pChannelMap, size_t channelMapCap)
MA_API void ma_engine_listener_set_direction(ma_engine *pEngine, ma_uint32 listenerIndex, float x, float y, float z)
struct ma_loshelf2_config ma_loshelf_config
MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config *pConfig, size_t *pHeapSizeInBytes)
ma_standard_sample_rate
Definition miniaudio.h:4298
@ ma_standard_sample_rate_44100
Definition miniaudio.h:4301
@ ma_standard_sample_rate_max
Definition miniaudio.h:4320
@ ma_standard_sample_rate_176400
Definition miniaudio.h:4309
@ ma_standard_sample_rate_min
Definition miniaudio.h:4319
@ ma_standard_sample_rate_24000
Definition miniaudio.h:4304
@ ma_standard_sample_rate_352800
Definition miniaudio.h:4316
@ ma_standard_sample_rate_32000
Definition miniaudio.h:4303
@ ma_standard_sample_rate_count
Definition miniaudio.h:4321
@ ma_standard_sample_rate_8000
Definition miniaudio.h:4314
@ ma_standard_sample_rate_192000
Definition miniaudio.h:4310
@ ma_standard_sample_rate_88200
Definition miniaudio.h:4307
@ ma_standard_sample_rate_16000
Definition miniaudio.h:4312
@ ma_standard_sample_rate_48000
Definition miniaudio.h:4300
@ ma_standard_sample_rate_96000
Definition miniaudio.h:4308
@ ma_standard_sample_rate_11025
Definition miniaudio.h:4313
@ ma_standard_sample_rate_384000
Definition miniaudio.h:4317
@ ma_standard_sample_rate_22050
Definition miniaudio.h:4305
MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform *pWaveform, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead)
struct ma_hishelf2_config ma_hishelf_config
void(* ma_log_callback_proc)(void *pUserData, ma_uint32 level, const char *pMessage)
Definition miniaudio.h:4538
MA_API ma_result ma_fader_init(const ma_fader_config *pConfig, ma_fader *pFader)
MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config *pConfig, size_t *pHeapSizeInBytes)
MA_API ma_result ma_sound_stop(ma_sound *pSound)
MA_API ma_paged_audio_buffer_page * ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data *pData)
MA_API ma_context * ma_device_get_context(ma_device *pDevice)
MA_API ma_result ma_hpf_init(const ma_hpf_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_hpf *pHPF)
MA_API ma_uint64 ma_node_get_time(const ma_node *pNode)
MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config *pConfig, void *pHeap, ma_lpf2 *pHPF)
MA_API ma_result ma_encoder_init_file_w(const wchar_t *pFilePath, const ma_encoder_config *pConfig, ma_encoder *pEncoder)
MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config *pConfig, size_t *pHeapSizeInBytes)
MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound *pSound, ma_uint64 fadeLengthInFrames)
ma_node_state
Definition miniaudio.h:10641
@ ma_node_state_started
Definition miniaudio.h:10642
@ ma_node_state_stopped
Definition miniaudio.h:10643
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)
MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener *pListener)
MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
#define MA_INLINE
Definition miniaudio.h:3986
MA_API ma_result ma_rb_seek_write(ma_rb *pRB, size_t offsetInBytes)
MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16 *pDst, const ma_int32 *pSrc, ma_uint64 count, float volume)
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)
MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config *pConfig, void *pHeap, ma_lpf1 *pLPF)
ma_uint16 ma_wchar_win32
Definition miniaudio.h:4044
MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave *pWaveform, ma_uint64 frameIndex)
MA_API ma_result ma_node_set_time(ma_node *pNode, ma_uint64 localTime)
MA_API ma_vec3f ma_sound_get_direction(const ma_sound *pSound)
MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer *pAudioBuffer, ma_uint64 frameCount)
MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler *pResampler)
MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config *pConfig, size_t *pHeapSizeInBytes)
#define MA_ATTRIBUTE_FORMAT(fmt, va)
Definition miniaudio.h:4506
MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer *pAudioBuffer, ma_uint64 *pLength)
MA_API void ma_panner_set_pan(ma_panner *pPanner, float pan)
void * ma_handle
Definition miniaudio.h:3830
MA_API ma_result ma_vfs_read(ma_vfs *pVFS, ma_vfs_file file, void *pDst, size_t sizeInBytes, size_t *pBytesRead)
ma_positioning
Definition miniaudio.h:5138
@ ma_positioning_relative
Definition miniaudio.h:5140
@ ma_positioning_absolute
Definition miniaudio.h:5139
MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group *pGroup)
struct ma_hpf1_config ma_hpf2_config
MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source *pDataSource, ma_uint64 *pLength)
double ma_double
Definition miniaudio.h:3828
MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler *pResampler, float ratioInOut)
ma_waveform_type
Definition miniaudio.h:10158
@ ma_waveform_type_square
Definition miniaudio.h:10160
@ ma_waveform_type_sawtooth
Definition miniaudio.h:10162
@ ma_waveform_type_sine
Definition miniaudio.h:10159
@ ma_waveform_type_triangle
Definition miniaudio.h:10161
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)
MA_API ma_data_source * ma_data_source_get_next(const ma_data_source *pDataSource)
MA_API const char * ma_get_format_name(ma_format format)
struct ma_notch2_config ma_notch_config
#define MA_LISTENER_INDEX_CLOSEST
Definition miniaudio.h:11118
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)
MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config *pConfig, size_t *pHeapSizeInBytes)
MA_API ma_result ma_log_register_callback(ma_log *pLog, ma_log_callback callback)
MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound *pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)
MA_API float ma_sound_get_max_distance(const ma_sound *pSound)
MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config *pConfig, size_t *pHeapSizeInBytes)
MA_API const char * ma_log_level_to_string(ma_uint32 logLevel)
MA_API ma_result ma_resource_manager_register_file(ma_resource_manager *pResourceManager, const char *pFilePath, ma_uint32 flags)
MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config *pConfig, size_t *pHeapSizeInBytes)
MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config *pConfig, ma_audio_buffer *pAudioBuffer)
MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener *pListener)
MA_API void ma_hpf_uninit(ma_hpf *pHPF, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API ma_result ma_lpf1_init(const ma_lpf1_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_lpf1 *pLPF)
MA_API ma_result ma_panner_init(const ma_panner_config *pConfig, ma_panner *pPanner)
struct ma_resource_manager ma_resource_manager
Definition miniaudio.h:10289
MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config *pConfig, ma_lpf2 *pLPF)
MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer *pDataBuffer, ma_bool32 isLooping)
MA_API void ma_sound_set_doppler_factor(ma_sound *pSound, float dopplerFactor)
MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_slot_allocator *pAllocator)
MA_API ma_result ma_context_enumerate_devices(ma_context *pContext, ma_enum_devices_callback_proc callback, void *pUserData)
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)
MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
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)
MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer *pAudioBuffer)
MA_API float ma_sound_group_get_pan(const ma_sound_group *pGroup)
MA_API ma_result ma_fader_process_pcm_frames(ma_fader *pFader, void *pFramesOut, const void *pFramesIn, ma_uint64 frameCount)
MA_API ma_result ma_event_init(ma_event *pEvent)
#define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE
Definition miniaudio.h:7060
struct ma_engine ma_engine
Definition miniaudio.h:11093
#define MA_MAX_DEVICE_NAME_LENGTH
Definition miniaudio.h:7063
MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
ma_result(* ma_encoder_write_proc)(ma_encoder *pEncoder, const void *pBufferIn, size_t bytesToWrite, size_t *pBytesWritten)
Definition miniaudio.h:10103
MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group *pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)
MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config *pConfig, ma_resource_manager *pResourceManager)
MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener *pListener, const ma_allocation_callbacks *pAllocationCallbacks)
MA_API void ma_free(void *p, const ma_allocation_callbacks *pAllocationCallbacks)
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)
MA_API ma_result ma_gainer_get_master_volume(const ma_gainer *pGainer, float *pVolume)
MA_API ma_result ma_log_post(ma_log *pLog, ma_uint32 level, const char *pMessage)
MA_API ma_result ma_encoder_init_vfs(ma_vfs *pVFS, const char *pFilePath, const ma_encoder_config *pConfig, ma_encoder *pEncoder)
MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer *pPagedAudioBuffer, ma_uint64 *pLength)
MA_API void ma_bpf2_uninit(ma_bpf2 *pBPF, const ma_allocation_callbacks *pAllocationCallbacks)
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)
MA_API ma_result ma_sound_seek_to_second(ma_sound *pSound, float seekPointInSeconds)
MA_API void ma_pcm_s16_to_s32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_API ma_result ma_noise_set_seed(ma_noise *pNoise, ma_int32 seed)
MA_API float ma_sound_group_get_pitch(const ma_sound_group *pGroup)
MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8 *pDst, const ma_int16 *pSrc, ma_uint64 count, float volume)
MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener *pListener, float *pInnerAngleInRadians, float *pOuterAngleInRadians, float *pOuterGain)
MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb *pRB, ma_uint32 *pSizeInFrames, void **ppBufferOut)
MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
MA_API float ma_sound_get_pitch(const ma_sound *pSound)
MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer *pSpatializer)
MA_API void ma_apply_volume_factor_pcm_frames_s24(void *pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
struct ma_job ma_job
Definition miniaudio.h:6366
MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf *pLPF)
MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node *pDataSourceNode, ma_bool32 isLooping)
uint16_t in
Definition mixer.c:80
uint16_t nbytes
Definition mixer.c:82
uint16_t out
Definition mixer.c:81
uint16_t rate[2]
Definition mixer.c:85
union @111115220225173102313045065244126153342261214353::@304166376113356371145235255022050055005326174300 buf
@ s16
Definition GenericArray.h:55
@ f32
Definition GenericArray.h:59
@ u32
Definition GenericArray.h:56
Definition portable-file-dialogs.h:70
@ info
Definition portable-file-dialogs.h:92
@ error
Definition portable-file-dialogs.h:94
u32 fill[2]
Definition data.c:141
#define va_end
Definition stdarg.h:9
#define va_list
Definition stdarg.h:6
#define va_start
Definition stdarg.h:7
size_t strlen(const char *str)
Definition string.c:14
void * memcpy(void *dst, const void *src, size_t size)
Definition string.c:4
Definition miniaudio.h:4354
void * pUserData
Definition miniaudio.h:4355
void *(* onMalloc)(size_t sz, void *pUserData)
Definition miniaudio.h:4356
void(* onFree)(void *p, void *pUserData)
Definition miniaudio.h:4358
void *(* onRealloc)(void *p, size_t sz, void *pUserData)
Definition miniaudio.h:4357
Definition miniaudio.h:6268
void(* onSignal)(ma_async_notification *pNotification)
Definition miniaudio.h:6269
Definition miniaudio.h:6296
ma_event e
Definition miniaudio.h:6299
ma_async_notification_callbacks cb
Definition miniaudio.h:6297
Definition miniaudio.h:6281
ma_bool32 signalled
Definition miniaudio.h:6283
ma_async_notification_callbacks cb
Definition miniaudio.h:6282
Definition miniaudio.h:5124
ma_vec3f v
Definition miniaudio.h:5125
ma_spinlock lock
Definition miniaudio.h:5126
Definition miniaudio.h:5903
const void * pData
Definition miniaudio.h:5908
ma_uint32 channels
Definition miniaudio.h:5905
ma_allocation_callbacks allocationCallbacks
Definition miniaudio.h:5909
ma_format format
Definition miniaudio.h:5904
ma_uint32 sampleRate
Definition miniaudio.h:5906
ma_uint64 sizeInFrames
Definition miniaudio.h:5907
Definition miniaudio.h:5878
ma_uint64 sizeInFrames
Definition miniaudio.h:5884
ma_uint32 sampleRate
Definition miniaudio.h:5882
ma_uint32 channels
Definition miniaudio.h:5881
ma_format format
Definition miniaudio.h:5880
ma_data_source_base ds
Definition miniaudio.h:5879
ma_uint64 cursor
Definition miniaudio.h:5883
const void * pData
Definition miniaudio.h:5885
Definition miniaudio.h:5915
ma_audio_buffer_ref ref
Definition miniaudio.h:5916
ma_allocation_callbacks allocationCallbacks
Definition miniaudio.h:5917
ma_uint8 _pExtraData[1]
Definition miniaudio.h:5919
ma_bool32 ownsData
Definition miniaudio.h:5918
Definition miniaudio.h:7275
ma_result(* onDeviceStop)(ma_device *pDevice)
Definition miniaudio.h:7283
ma_result(* onDeviceUninit)(ma_device *pDevice)
Definition miniaudio.h:7281
ma_result(* onDeviceWrite)(ma_device *pDevice, const void *pFrames, ma_uint32 frameCount, ma_uint32 *pFramesWritten)
Definition miniaudio.h:7285
ma_result(* onDeviceRead)(ma_device *pDevice, void *pFrames, ma_uint32 frameCount, ma_uint32 *pFramesRead)
Definition miniaudio.h:7284
ma_result(* onDeviceInit)(ma_device *pDevice, const ma_device_config *pConfig, ma_device_descriptor *pDescriptorPlayback, ma_device_descriptor *pDescriptorCapture)
Definition miniaudio.h:7280
ma_result(* onDeviceGetInfo)(ma_device *pDevice, ma_device_type type, ma_device_info *pDeviceInfo)
Definition miniaudio.h:7288
ma_result(* onDeviceStart)(ma_device *pDevice)
Definition miniaudio.h:7282
ma_result(* onContextInit)(ma_context *pContext, const ma_context_config *pConfig, ma_backend_callbacks *pCallbacks)
Definition miniaudio.h:7276
ma_result(* onDeviceDataLoop)(ma_device *pDevice)
Definition miniaudio.h:7286
ma_result(* onDeviceDataLoopWakeup)(ma_device *pDevice)
Definition miniaudio.h:7287
ma_result(* onContextUninit)(ma_context *pContext)
Definition miniaudio.h:7277
ma_result(* onContextGetDeviceInfo)(ma_context *pContext, ma_device_type deviceType, const ma_device_id *pDeviceID, ma_device_info *pDeviceInfo)
Definition miniaudio.h:7279
ma_result(* onContextEnumerateDevices)(ma_context *pContext, ma_enum_devices_callback_proc callback, void *pUserData)
Definition miniaudio.h:7278
Definition miniaudio.h:4580
double b2
Definition miniaudio.h:4585
double a2
Definition miniaudio.h:4588
double b0
Definition miniaudio.h:4583
ma_uint32 channels
Definition miniaudio.h:4582
double a0
Definition miniaudio.h:4586
ma_format format
Definition miniaudio.h:4581
double b1
Definition miniaudio.h:4584
double a1
Definition miniaudio.h:4587
Definition miniaudio.h:10880
ma_node_config nodeConfig
Definition miniaudio.h:10881
ma_biquad_config biquad
Definition miniaudio.h:10882
Definition miniaudio.h:10889
ma_node_base baseNode
Definition miniaudio.h:10890
ma_biquad biquad
Definition miniaudio.h:10891
Definition miniaudio.h:4594
ma_biquad_coefficient b0
Definition miniaudio.h:4597
ma_biquad_coefficient a1
Definition miniaudio.h:4600
ma_biquad_coefficient a2
Definition miniaudio.h:4601
ma_biquad_coefficient b1
Definition miniaudio.h:4598
ma_format format
Definition miniaudio.h:4595
void * _pHeap
Definition miniaudio.h:4606
ma_biquad_coefficient b2
Definition miniaudio.h:4599
ma_biquad_coefficient * pR2
Definition miniaudio.h:4603
ma_biquad_coefficient * pR1
Definition miniaudio.h:4602
ma_bool32 _ownsHeap
Definition miniaudio.h:4607
ma_uint32 channels
Definition miniaudio.h:4596
Definition miniaudio.h:4801
ma_format format
Definition miniaudio.h:4802
ma_uint32 sampleRate
Definition miniaudio.h:4804
double q
Definition miniaudio.h:4806
ma_uint32 channels
Definition miniaudio.h:4803
double cutoffFrequency
Definition miniaudio.h:4805
Definition miniaudio.h:4812
ma_biquad bq
Definition miniaudio.h:4813
Definition miniaudio.h:4826
ma_uint32 channels
Definition miniaudio.h:4828
ma_format format
Definition miniaudio.h:4827
ma_uint32 sampleRate
Definition miniaudio.h:4829
ma_uint32 order
Definition miniaudio.h:4831
double cutoffFrequency
Definition miniaudio.h:4830
Definition miniaudio.h:10949
ma_bpf_config bpf
Definition miniaudio.h:10951
ma_node_config nodeConfig
Definition miniaudio.h:10950
Definition miniaudio.h:10958
ma_bpf bpf
Definition miniaudio.h:10960
ma_node_base baseNode
Definition miniaudio.h:10959
Definition miniaudio.h:4837
ma_uint32 channels
Definition miniaudio.h:4839
void * _pHeap
Definition miniaudio.h:4844
ma_bool32 _ownsHeap
Definition miniaudio.h:4845
ma_bpf2 * pBPF2
Definition miniaudio.h:4841
ma_uint32 bpf2Count
Definition miniaudio.h:4840
ma_format format
Definition miniaudio.h:4838
Definition miniaudio.h:5525
ma_bool32 calculateLFEFromSpatialChannels
Definition miniaudio.h:5532
ma_uint32 channelsOut
Definition miniaudio.h:5528
const ma_channel * pChannelMapOut
Definition miniaudio.h:5530
ma_uint32 channelsIn
Definition miniaudio.h:5527
ma_format format
Definition miniaudio.h:5526
const ma_channel * pChannelMapIn
Definition miniaudio.h:5529
ma_channel_mix_mode mixingMode
Definition miniaudio.h:5531
float ** ppWeights
Definition miniaudio.h:5533
Definition miniaudio.h:5539
ma_channel * pChannelMapOut
Definition miniaudio.h:5546
ma_bool32 _ownsHeap
Definition miniaudio.h:5556
union ma_channel_converter::@005141355056005101255344312273141300321243115125 weights
ma_channel_conversion_path conversionPath
Definition miniaudio.h:5544
ma_uint8 * pShuffleTable
Definition miniaudio.h:5547
float ** f32
Definition miniaudio.h:5550
ma_int32 ** s16
Definition miniaudio.h:5551
ma_channel * pChannelMapIn
Definition miniaudio.h:5545
ma_uint32 channelsOut
Definition miniaudio.h:5542
void * _pHeap
Definition miniaudio.h:5555
ma_format format
Definition miniaudio.h:5540
ma_uint32 channelsIn
Definition miniaudio.h:5541
ma_channel_mix_mode mixingMode
Definition miniaudio.h:5543
Definition miniaudio.h:7329
ma_result * pResult
Definition miniaudio.h:7343
union ma_context_command__wasapi::@326047373024335215026041147115257354111101050025 data
ma_device * pDevice
Definition miniaudio.h:7347
void * pAudioClient
Definition miniaudio.h:7341
int _unused
Definition miniaudio.h:7336
struct ma_context_command__wasapi::@326047373024335215026041147115257354111101050025::@113002107176003053222266213010305073044326147255 releaseAudioClient
ma_event * pEvent
Definition miniaudio.h:7331
void ** ppAudioClientService
Definition miniaudio.h:7342
struct ma_context_command__wasapi::@326047373024335215026041147115257354111101050025::@274277072165370004373265173121360233270075340222 createAudioClient
int code
Definition miniaudio.h:7330
ma_device_type deviceType
Definition miniaudio.h:7340
Definition miniaudio.h:7292
ma_uint32 sessionCategoryOptions
Definition miniaudio.h:7315
ma_bool32 noAudioSessionActivate
Definition miniaudio.h:7316
ma_thread_priority threadPriority
Definition miniaudio.h:7294
ma_bool32 noAudioSessionDeactivate
Definition miniaudio.h:7317
struct ma_context_config::@232051224367103246136016352073110017213316242306 alsa
const char * pClientName
Definition miniaudio.h:7321
ma_backend_callbacks custom
Definition miniaudio.h:7324
void * pUserData
Definition miniaudio.h:7296
struct ma_context_config::@350105251245236374155212033305301016314070316004 pulse
struct ma_context_config::@362133220040320302125000160373346007112043317244 coreaudio
ma_allocation_callbacks allocationCallbacks
Definition miniaudio.h:7297
const char * pApplicationName
Definition miniaudio.h:7308
ma_handle hWnd
Definition miniaudio.h:7300
ma_bool32 tryAutoSpawn
Definition miniaudio.h:7310
ma_bool32 tryStartServer
Definition miniaudio.h:7322
const char * pServerName
Definition miniaudio.h:7309
ma_log * pLog
Definition miniaudio.h:7293
ma_bool32 useVerboseDeviceEnumeration
Definition miniaudio.h:7304
struct ma_context_config::@010237040337162363140053340032050234350170246006 dsound
size_t threadStackSize
Definition miniaudio.h:7295
struct ma_context_config::@076165314314004117063310320114306017044150355166 jack
ma_ios_session_category sessionCategory
Definition miniaudio.h:7314
Definition miniaudio.h:7354
int _unused
Definition miniaudio.h:7721
ma_uint32 playbackDeviceInfoCount
Definition miniaudio.h:7366
size_t threadStackSize
Definition miniaudio.h:7360
ma_backend_callbacks callbacks
Definition miniaudio.h:7355
ma_uint32 deviceInfoCapacity
Definition miniaudio.h:7365
ma_device_info * pDeviceInfos
Definition miniaudio.h:7368
struct ma_context::@047007154360060024315013127110130313171237113123::@167107304135021354336302272331116171025130104223 posix
ma_thread_priority threadPriority
Definition miniaudio.h:7359
ma_mutex deviceEnumLock
Definition miniaudio.h:7363
ma_backend backend
Definition miniaudio.h:7356
ma_allocation_callbacks allocationCallbacks
Definition miniaudio.h:7362
ma_log log
Definition miniaudio.h:7358
ma_uint32 captureDeviceInfoCount
Definition miniaudio.h:7367
struct ma_context::@206337375302132312004326126134362343052164217255::@376131055345067162221221257020354152170060250011 null_backend
ma_mutex deviceInfoLock
Definition miniaudio.h:7364
ma_log * pLog
Definition miniaudio.h:7357
void * pUserData
Definition miniaudio.h:7361
Definition miniaudio.h:5574
ma_uint32 sampleRateIn
Definition miniaudio.h:5579
ma_uint32 channelsOut
Definition miniaudio.h:5578
ma_format formatOut
Definition miniaudio.h:5576
ma_bool32 allowDynamicSampleRate
Definition miniaudio.h:5587
ma_channel_mix_mode channelMixMode
Definition miniaudio.h:5584
ma_bool32 calculateLFEFromSpatialChannels
Definition miniaudio.h:5585
ma_resampler_config resampling
Definition miniaudio.h:5588
ma_dither_mode ditherMode
Definition miniaudio.h:5583
ma_channel * pChannelMapIn
Definition miniaudio.h:5581
float ** ppChannelWeights
Definition miniaudio.h:5586
ma_format formatIn
Definition miniaudio.h:5575
ma_channel * pChannelMapOut
Definition miniaudio.h:5582
ma_uint32 channelsIn
Definition miniaudio.h:5577
ma_uint32 sampleRateOut
Definition miniaudio.h:5580
Definition miniaudio.h:5606
ma_bool8 hasResampler
Definition miniaudio.h:5620
ma_uint32 sampleRateOut
Definition miniaudio.h:5612
ma_format formatOut
Definition miniaudio.h:5608
void * _pHeap
Definition miniaudio.h:5625
ma_uint32 channelsOut
Definition miniaudio.h:5610
ma_uint32 channelsIn
Definition miniaudio.h:5609
ma_format formatIn
Definition miniaudio.h:5607
ma_channel_converter channelConverter
Definition miniaudio.h:5615
ma_uint32 sampleRateIn
Definition miniaudio.h:5611
ma_bool8 _ownsHeap
Definition miniaudio.h:5624
ma_bool8 hasChannelConverter
Definition miniaudio.h:5619
ma_dither_mode ditherMode
Definition miniaudio.h:5613
ma_bool8 hasPostFormatConversion
Definition miniaudio.h:5618
ma_resampler resampler
Definition miniaudio.h:5616
ma_bool8 isPassthrough
Definition miniaudio.h:5621
ma_bool8 hasPreFormatConversion
Definition miniaudio.h:5617
ma_data_converter_execution_path executionPath
Definition miniaudio.h:5614
Definition miniaudio.h:5839
ma_data_source * pNext
Definition miniaudio.h:5846
ma_data_source_get_next_proc onGetNext
Definition miniaudio.h:5847
ma_uint64 loopBegInFrames
Definition miniaudio.h:5843
ma_data_source * pCurrent
Definition miniaudio.h:5845
ma_uint64 rangeBegInFrames
Definition miniaudio.h:5841
ma_uint64 loopEndInFrames
Definition miniaudio.h:5844
MA_ATOMIC(4, ma_bool32) isLooping
const ma_data_source_vtable * vtable
Definition miniaudio.h:5840
ma_uint64 rangeEndInFrames
Definition miniaudio.h:5842
Definition miniaudio.h:5831
const ma_data_source_vtable * vtable
Definition miniaudio.h:5832
Definition miniaudio.h:10836
ma_node_config nodeConfig
Definition miniaudio.h:10837
ma_data_source * pDataSource
Definition miniaudio.h:10838
Definition miniaudio.h:10845
ma_node_base base
Definition miniaudio.h:10846
ma_data_source * pDataSource
Definition miniaudio.h:10847
Definition miniaudio.h:5818
ma_result(* onRead)(ma_data_source *pDataSource, void *pFramesOut, ma_uint64 frameCount, ma_uint64 *pFramesRead)
Definition miniaudio.h:5819
ma_result(* onGetLength)(ma_data_source *pDataSource, ma_uint64 *pLength)
Definition miniaudio.h:5823
ma_result(* onSetLooping)(ma_data_source *pDataSource, ma_bool32 isLooping)
Definition miniaudio.h:5824
ma_result(* onGetCursor)(ma_data_source *pDataSource, ma_uint64 *pCursor)
Definition miniaudio.h:5822
ma_result(* onSeek)(ma_data_source *pDataSource, ma_uint64 frameIndex)
Definition miniaudio.h:5820
ma_result(* onGetDataFormat)(ma_data_source *pDataSource, ma_format *pFormat, ma_uint32 *pChannels, ma_uint32 *pSampleRate, ma_channel *pChannelMap, size_t channelMapCap)
Definition miniaudio.h:5821
ma_uint32 flags
Definition miniaudio.h:5825
Definition miniaudio.h:9963
ma_dither_mode ditherMode
Definition miniaudio.h:9969
ma_resampler_config resampling
Definition miniaudio.h:9970
ma_encoding_format encodingFormat
Definition miniaudio.h:9972
ma_uint32 customBackendCount
Definition miniaudio.h:9975
ma_channel_mix_mode channelMixMode
Definition miniaudio.h:9968
ma_uint32 seekPointCount
Definition miniaudio.h:9973
ma_format format
Definition miniaudio.h:9964
ma_channel * pChannelMap
Definition miniaudio.h:9967
ma_allocation_callbacks allocationCallbacks
Definition miniaudio.h:9971
ma_uint32 channels
Definition miniaudio.h:9965
ma_decoding_backend_vtable ** ppCustomBackendVTables
Definition miniaudio.h:9974
void * pCustomBackendUserData
Definition miniaudio.h:9976
ma_uint32 sampleRate
Definition miniaudio.h:9966
Definition miniaudio.h:9980
size_t currentReadPos
Definition miniaudio.h:10010
const ma_decoding_backend_vtable * pBackendVTable
Definition miniaudio.h:9983
ma_uint64 inputCacheConsumed
Definition miniaudio.h:9996
void * pInputCache
Definition miniaudio.h:9994
ma_uint32 outputChannels
Definition miniaudio.h:9991
struct ma_decoder::@233102246220004263261046156045057336106373054042::@216212317366315156007350203352165143070252371202 vfs
ma_uint64 readPointerInPCMFrames
Definition miniaudio.h:9989
ma_data_converter converter
Definition miniaudio.h:9993
ma_uint64 inputCacheRemaining
Definition miniaudio.h:9997
ma_format outputFormat
Definition miniaudio.h:9990
ma_uint32 outputSampleRate
Definition miniaudio.h:9992
struct ma_decoder::@233102246220004263261046156045057336106373054042::@167333022324226261357002240250045341215152253040 memory
void * pBackendUserData
Definition miniaudio.h:9984
union ma_decoder::@233102246220004263261046156045057336106373054042 data
ma_data_source * pBackend
Definition miniaudio.h:9982
ma_decoder_tell_proc onTell
Definition miniaudio.h:9987
const ma_uint8 * pData
Definition miniaudio.h:10008
ma_vfs_file file
Definition miniaudio.h:10004
ma_allocation_callbacks allocationCallbacks
Definition miniaudio.h:9998
ma_decoder_read_proc onRead
Definition miniaudio.h:9985
ma_uint64 inputCacheCap
Definition miniaudio.h:9995
void * pUserData
Definition miniaudio.h:9988
ma_decoder_seek_proc onSeek
Definition miniaudio.h:9986
size_t dataSize
Definition miniaudio.h:10009
ma_data_source_base ds
Definition miniaudio.h:9981
ma_vfs * pVFS
Definition miniaudio.h:10003
Definition miniaudio.h:9940
ma_format preferredFormat
Definition miniaudio.h:9941
ma_uint32 seekPointCount
Definition miniaudio.h:9942
Definition miniaudio.h:9949
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)
Definition miniaudio.h:9950
ma_result(* onInitFileW)(void *pUserData, const wchar_t *pFilePath, const ma_decoding_backend_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_data_source **ppBackend)
Definition miniaudio.h:9952
ma_result(* onInitFile)(void *pUserData, const char *pFilePath, const ma_decoding_backend_config *pConfig, const ma_allocation_callbacks *pAllocationCallbacks, ma_data_source **ppBackend)
Definition miniaudio.h:9951
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)
Definition miniaudio.h:9953
void(* onUninit)(void *pUserData, ma_data_source *pBackend, const ma_allocation_callbacks *pAllocationCallbacks)
Definition miniaudio.h:9954
Definition miniaudio.h:9900
ma_vfs_callbacks cb
Definition miniaudio.h:9901
ma_allocation_callbacks allocationCallbacks
Definition miniaudio.h:9902
Definition miniaudio.h:4985
float dry
Definition miniaudio.h:4991
ma_uint32 sampleRate
Definition miniaudio.h:4987
ma_uint32 channels
Definition miniaudio.h:4986
ma_uint32 delayInFrames
Definition miniaudio.h:4988
ma_bool32 delayStart
Definition miniaudio.h:4989
float wet
Definition miniaudio.h:4990
float decay
Definition miniaudio.h:4992
Definition miniaudio.h:11061
ma_node_config nodeConfig
Definition miniaudio.h:11062
ma_delay_config delay
Definition miniaudio.h:11063
Definition miniaudio.h:11070
ma_node_base baseNode
Definition miniaudio.h:11071
ma_delay delay
Definition miniaudio.h:11072
Definition miniaudio.h:4999
ma_delay_config config
Definition miniaudio.h:5000
ma_uint32 cursor
Definition miniaudio.h:5001
ma_uint32 bufferSizeInFrames
Definition miniaudio.h:5002
float * pBuffer
Definition miniaudio.h:5003
Definition miniaudio.h:7084
struct ma_device_config::@324314104335345110154331005046034231161210245070 aaudio
ma_opensl_stream_type streamType
Definition miniaudio.h:7150
ma_channel * pChannelMap
Definition miniaudio.h:7105
ma_share_mode shareMode
Definition miniaudio.h:7108
ma_aaudio_content_type contentType
Definition miniaudio.h:7157
ma_device_type deviceType
Definition miniaudio.h:7085
struct ma_device_config::@367175325147274237257225166163020242231270067114 coreaudio
ma_bool32 noAutoStartAfterReroute
Definition miniaudio.h:7160
ma_channel_mix_mode channelMixMode
Definition miniaudio.h:7106
int channelMap
Definition miniaudio.h:7142
ma_resampler_config resampling
Definition miniaudio.h:7099
ma_performance_profile performanceProfile
Definition miniaudio.h:7090
void * pUserData
Definition miniaudio.h:7098
ma_device_data_proc dataCallback
Definition miniaudio.h:7095
ma_device_notification_proc notificationCallback
Definition miniaudio.h:7096
ma_aaudio_input_preset inputPreset
Definition miniaudio.h:7158
const char * pStreamNameCapture
Definition miniaudio.h:7141
ma_uint32 sampleRate
Definition miniaudio.h:7086
const ma_device_id * pDeviceID
Definition miniaudio.h:7102
ma_bool32 noAutoChannels
Definition miniaudio.h:7135
struct ma_device_config::@074051314032330056204017123231337153350122350140 pulse
ma_bool32 allowNominalSampleRateChange
Definition miniaudio.h:7146
ma_bool32 allowSetBufferCapacity
Definition miniaudio.h:7162
ma_bool32 enableCompatibilityWorkarounds
Definition miniaudio.h:7152
struct ma_device_config::@247362324145200276214317214033151175267315032004 opensl
struct ma_device_config::@352351140123201317235124122073053103136267056163 playback
ma_uint32 periodSizeInFrames
Definition miniaudio.h:7087
ma_uint32 periodSizeInMilliseconds
Definition miniaudio.h:7088
ma_stop_proc stopCallback
Definition miniaudio.h:7097
ma_bool32 calculateLFEFromSpatialChannels
Definition miniaudio.h:7107
ma_bool8 noDefaultQualitySRC
Definition miniaudio.h:7125
ma_bool32 noMMap
Definition miniaudio.h:7133
ma_wasapi_usage usage
Definition miniaudio.h:7123
ma_bool8 noHardwareOffloading
Definition miniaudio.h:7127
ma_bool8 noAutoStreamRouting
Definition miniaudio.h:7126
ma_bool32 noAutoFormat
Definition miniaudio.h:7134
ma_uint32 loopbackProcessID
Definition miniaudio.h:7128
ma_bool8 noFixedSizedCallback
Definition miniaudio.h:7094
ma_bool8 noClip
Definition miniaudio.h:7092
struct ma_device_config::@240223127140255370243010246217306012046142211301 wasapi
ma_uint32 periods
Definition miniaudio.h:7089
ma_aaudio_allowed_capture_policy allowedCapturePolicy
Definition miniaudio.h:7159
ma_bool8 noDisableDenormals
Definition miniaudio.h:7093
ma_uint32 channels
Definition miniaudio.h:7104
const char * pStreamNamePlayback
Definition miniaudio.h:7140
ma_format format
Definition miniaudio.h:7103
ma_bool32 noAutoResample
Definition miniaudio.h:7136
ma_bool8 noAutoConvertSRC
Definition miniaudio.h:7124
ma_bool8 noPreSilencedOutputBuffer
Definition miniaudio.h:7091
ma_bool8 loopbackProcessExclude
Definition miniaudio.h:7129
struct ma_device_config::@156210126371174224255045114175112216136106142307 capture
ma_opensl_recording_preset recordingPreset
Definition miniaudio.h:7151
struct ma_device_config::@120014253245124370256070151037263131267013366261 alsa
Definition miniaudio.h:7194
ma_channel channelMap[MA_MAX_CHANNELS]
Definition miniaudio.h:7200
ma_uint32 periodSizeInMilliseconds
Definition miniaudio.h:7202
ma_uint32 periodCount
Definition miniaudio.h:7203
ma_share_mode shareMode
Definition miniaudio.h:7196
ma_uint32 sampleRate
Definition miniaudio.h:7199
ma_uint32 periodSizeInFrames
Definition miniaudio.h:7201
ma_uint32 channels
Definition miniaudio.h:7198
const ma_device_id * pDeviceID
Definition miniaudio.h:7195
ma_format format
Definition miniaudio.h:7197
Definition miniaudio.h:7067
ma_uint32 flags
Definition miniaudio.h:7079
ma_uint32 channels
Definition miniaudio.h:7077
ma_bool32 isDefault
Definition miniaudio.h:7071
char name[MA_MAX_DEVICE_NAME_LENGTH+1]
Definition miniaudio.h:7070
ma_device_id id
Definition miniaudio.h:7069
struct ma_device_info::@006306212266135276045300303147351076047256200226 nativeDataFormats[/*ma_format_count *ma_standard_sample_rate_count *MA_MAX_CHANNELS */64]
ma_uint32 sampleRate
Definition miniaudio.h:7078
ma_format format
Definition miniaudio.h:7076
ma_uint32 nativeDataFormatCount
Definition miniaudio.h:7073
Definition miniaudio.h:6734
ma_uint32 jobQueueCapacity
Definition miniaudio.h:6736
ma_bool32 noThread
Definition miniaudio.h:6735
ma_uint32 jobQueueFlags
Definition miniaudio.h:6737
Definition miniaudio.h:6743
ma_job_queue jobQueue
Definition miniaudio.h:6745
ma_thread thread
Definition miniaudio.h:6744
ma_bool32 _hasThread
Definition miniaudio.h:6746
Definition miniaudio.h:6768
ma_device * pDevice
Definition miniaudio.h:6769
ma_device_notification_type type
Definition miniaudio.h:6770
int _unused
Definition miniaudio.h:6775
Definition miniaudio.h:7763
ma_result operationResult
Definition miniaudio.h:8047
ma_uint32 intermediaryBufferCap
Definition miniaudio.h:7814
ma_bool8 noClip
Definition miniaudio.h:7780
char name[MA_MAX_DEVICE_NAME_LENGTH+1]
Definition miniaudio.h:7799
ma_format format
Definition miniaudio.h:7801
ma_uint32 intermediaryBufferLen
Definition miniaudio.h:7815
void * pBackendUserData
Definition miniaudio.h:7789
void * pUserData
Definition miniaudio.h:7771
ma_device_data_proc onData
Definition miniaudio.h:7768
ma_bool8 noDisableDenormals
Definition miniaudio.h:7781
ma_device_notification_proc onNotification
Definition miniaudio.h:7769
ma_context * pContext
Definition miniaudio.h:7764
ma_uint64 lastProcessedFrameCapture
Definition miniaudio.h:8053
ma_uint32 currentPeriodFramesRemainingCapture
Definition miniaudio.h:8051
ma_bool8 noFixedSizedCallback
Definition miniaudio.h:7782
ma_bool8 noPreSilencedOutputBuffer
Definition miniaudio.h:7779
void * pIntermediaryBuffer
Definition miniaudio.h:7813
ma_duplex_rb duplexRB
Definition miniaudio.h:7784
ma_uint32 internalSampleRate
Definition miniaudio.h:7806
ma_result workResult
Definition miniaudio.h:7777
ma_uint32 internalPeriods
Definition miniaudio.h:7809
ma_device_id * pID
Definition miniaudio.h:7797
ma_mutex startStopLock
Definition miniaudio.h:7772
ma_event startEvent
Definition miniaudio.h:7774
ma_event stopEvent
Definition miniaudio.h:7775
ma_semaphore operationSemaphore
Definition miniaudio.h:8045
ma_uint32 internalPeriodSizeInFrames
Definition miniaudio.h:7808
ma_atomic_float masterVolumeFactor
Definition miniaudio.h:7783
void * pInputCache
Definition miniaudio.h:7816
ma_uint32 lpfOrder
Definition miniaudio.h:7792
ma_data_converter converter
Definition miniaudio.h:7812
struct ma_device::@150203210306125235221100155302211026141311311003::@131021254215013326223011043261334263303371152201 linear
double priorRunTime
Definition miniaudio.h:8049
struct ma_device::@167231020037376173300366102374204255237270225126::@344313146217003171076104122123307056103037233077 null_device
ma_device_type type
Definition miniaudio.h:7765
ma_channel_mix_mode channelMixMode
Definition miniaudio.h:7810
ma_resample_algorithm algorithm
Definition miniaudio.h:7787
ma_atomic_device_state state
Definition miniaudio.h:7767
ma_channel internalChannelMap[MA_MAX_CHANNELS]
Definition miniaudio.h:7807
ma_resampling_backend_vtable * pBackendVTable
Definition miniaudio.h:7788
ma_event wakeupEvent
Definition miniaudio.h:7773
struct ma_device::@327102037004011332263172043116211076172352051363 capture
struct ma_device::@244072337315151147324252340036362355247157166371 playback
struct ma_device::@150203210306125235221100155302211026141311311003 resampling
ma_thread thread
Definition miniaudio.h:7776
ma_share_mode shareMode
Definition miniaudio.h:7800
ma_event operationCompletionEvent
Definition miniaudio.h:8044
ma_format internalFormat
Definition miniaudio.h:7804
ma_atomic_bool32 isStarted
Definition miniaudio.h:8054
ma_uint32 internalChannels
Definition miniaudio.h:7805
ma_uint32 channels
Definition miniaudio.h:7802
ma_uint32 operation
Definition miniaudio.h:8046
ma_bool8 isOwnerOfContext
Definition miniaudio.h:7778
ma_event operationEvent
Definition miniaudio.h:8043
ma_uint32 sampleRate
Definition miniaudio.h:7766
ma_thread deviceThread
Definition miniaudio.h:8042
ma_bool32 calculateLFEFromSpatialChannels
Definition miniaudio.h:7811
ma_uint64 inputCacheRemaining
Definition miniaudio.h:7819
ma_uint32 currentPeriodFramesRemainingPlayback
Definition miniaudio.h:8050
ma_device_id id
Definition miniaudio.h:7798
ma_timer timer
Definition miniaudio.h:8048
ma_uint64 inputCacheConsumed
Definition miniaudio.h:7818
ma_uint64 inputCacheCap
Definition miniaudio.h:7817
ma_stop_proc onStop
Definition miniaudio.h:7770
ma_uint64 lastProcessedFramePlayback
Definition miniaudio.h:8052
ma_channel channelMap[MA_MAX_CHANNELS]
Definition miniaudio.h:7803
Definition miniaudio.h:6081
ma_pcm_rb rb
Definition miniaudio.h:6082
Definition miniaudio.h:10110
ma_format format
Definition miniaudio.h:10112
ma_uint32 channels
Definition miniaudio.h:10113
ma_allocation_callbacks allocationCallbacks
Definition miniaudio.h:10115
ma_encoding_format encodingFormat
Definition miniaudio.h:10111
ma_uint32 sampleRate
Definition miniaudio.h:10114
Definition miniaudio.h:10121
ma_encoder_write_proc onWrite
Definition miniaudio.h:10123
ma_encoder_seek_proc onSeek
Definition miniaudio.h:10124
struct ma_encoder::@101255207020017305051232306111070071103271014317::@257067163117142321323137160321353322375312250173 vfs
ma_vfs * pVFS
Definition miniaudio.h:10134
void * pInternalEncoder
Definition miniaudio.h:10129
union ma_encoder::@101255207020017305051232306111070071103271014317 data
ma_encoder_write_pcm_frames_proc onWritePCMFrames
Definition miniaudio.h:10127
ma_vfs_file file
Definition miniaudio.h:10135
ma_encoder_uninit_proc onUninit
Definition miniaudio.h:10126
ma_encoder_init_proc onInit
Definition miniaudio.h:10125
ma_encoder_config config
Definition miniaudio.h:10122
void * pUserData
Definition miniaudio.h:10128
Definition miniaudio.h:11256
ma_uint32 gainSmoothTimeInMilliseconds
Definition miniaudio.h:11274
ma_uint32 preMixStackSizeInBytes
Definition miniaudio.h:11276
ma_vfs * pResourceManagerVFS
Definition miniaudio.h:11281
ma_uint32 listenerCount
Definition miniaudio.h:11268
ma_allocation_callbacks allocationCallbacks
Definition miniaudio.h:11277
ma_context * pContext
Definition miniaudio.h:11261
ma_device_id * pPlaybackDeviceID
Definition miniaudio.h:11263
void * pProcessUserData
Definition miniaudio.h:11283
ma_uint32 periodSizeInMilliseconds
Definition miniaudio.h:11272
ma_device_data_proc dataCallback
Definition miniaudio.h:11264
ma_bool32 noDevice
Definition miniaudio.h:11279
ma_device_notification_proc notificationCallback
Definition miniaudio.h:11265
ma_device * pDevice
Definition miniaudio.h:11262
ma_uint32 defaultVolumeSmoothTimeInPCMFrames
Definition miniaudio.h:11275
ma_engine_process_proc onProcess
Definition miniaudio.h:11282
ma_uint32 channels
Definition miniaudio.h:11269
ma_uint32 sampleRate
Definition miniaudio.h:11270
ma_resource_manager * pResourceManager
Definition miniaudio.h:11258
ma_uint32 periodSizeInFrames
Definition miniaudio.h:11271
ma_mono_expansion_mode monoExpansionMode
Definition miniaudio.h:11280
ma_log * pLog
Definition miniaudio.h:11267
ma_bool32 noAutoStart
Definition miniaudio.h:11278
ma_uint32 gainSmoothTimeInFrames
Definition miniaudio.h:11273
Definition miniaudio.h:11127
ma_engine * pEngine
Definition miniaudio.h:11128
ma_engine_node_type type
Definition miniaudio.h:11129
ma_bool8 isPitchDisabled
Definition miniaudio.h:11135
ma_uint32 sampleRate
Definition miniaudio.h:11132
ma_uint32 channelsOut
Definition miniaudio.h:11131
ma_mono_expansion_mode monoExpansionMode
Definition miniaudio.h:11134
ma_uint8 pinnedListenerIndex
Definition miniaudio.h:11137
ma_uint32 channelsIn
Definition miniaudio.h:11130
ma_bool8 isSpatializationDisabled
Definition miniaudio.h:11136
ma_uint32 volumeSmoothTimeInPCMFrames
Definition miniaudio.h:11133
Definition miniaudio.h:11145
struct ma_engine_node::@360013275337370070376361022026253014222142243004 fadeSettings
ma_atomic_float volumeBeg
Definition miniaudio.h:11167
MA_ATOMIC(4, ma_bool32) isSpatializationDisabled
ma_atomic_uint64 fadeLengthInFrames
Definition miniaudio.h:11169
ma_fader fader
Definition miniaudio.h:11151
ma_spatializer spatializer
Definition miniaudio.h:11153
ma_gainer volumeGainer
Definition miniaudio.h:11155
float oldPitch
Definition miniaudio.h:11158
MA_ATOMIC(4, ma_bool32) isPitchDisabled
MA_ATOMIC(4, float) pitch
ma_uint32 volumeSmoothTimeInPCMFrames
Definition miniaudio.h:11149
void * _pHeap
Definition miniaudio.h:11175
ma_atomic_float volumeEnd
Definition miniaudio.h:11168
ma_bool8 _ownsHeap
Definition miniaudio.h:11174
ma_atomic_float volume
Definition miniaudio.h:11156
ma_engine * pEngine
Definition miniaudio.h:11147
ma_linear_resampler resampler
Definition miniaudio.h:11152
float oldDopplerPitch
Definition miniaudio.h:11159
MA_ATOMIC(4, ma_uint32) pinnedListenerIndex
ma_uint32 sampleRate
Definition miniaudio.h:11148
ma_mono_expansion_mode monoExpansionMode
Definition miniaudio.h:11150
ma_panner panner
Definition miniaudio.h:11154
ma_node_base baseNode
Definition miniaudio.h:11146
ma_atomic_uint64 absoluteGlobalTimeInFrames
Definition miniaudio.h:11170
Definition miniaudio.h:11290
ma_uint32 gainSmoothTimeInFrames
Definition miniaudio.h:11308
ma_uint32 listenerCount
Definition miniaudio.h:11300
ma_bool8 ownsResourceManager
Definition miniaudio.h:11303
ma_log * pLog
Definition miniaudio.h:11298
ma_spatializer_listener listeners[MA_ENGINE_MAX_LISTENERS]
Definition miniaudio.h:11301
ma_spinlock inlinedSoundLock
Definition miniaudio.h:11305
ma_resource_manager * pResourceManager
Definition miniaudio.h:11293
ma_bool8 ownsDevice
Definition miniaudio.h:11304
ma_mono_expansion_mode monoExpansionMode
Definition miniaudio.h:11310
ma_device * pDevice
Definition miniaudio.h:11296
ma_allocation_callbacks allocationCallbacks
Definition miniaudio.h:11302
ma_engine_process_proc onProcess
Definition miniaudio.h:11311
ma_node_graph nodeGraph
Definition miniaudio.h:11291
ma_uint32 defaultVolumeSmoothTimeInPCMFrames
Definition miniaudio.h:11309
ma_uint32 sampleRate
Definition miniaudio.h:11299
MA_ATOMIC(4, ma_uint32) inlinedSoundCount
void * pProcessUserData
Definition miniaudio.h:11312
ma_sound_inlined * pInlinedSoundHead
Definition miniaudio.h:11306
Definition miniaudio.h:4455
ma_pthread_mutex_t lock
Definition miniaudio.h:4457
ma_uint32 value
Definition miniaudio.h:4456
ma_pthread_cond_t cond
Definition miniaudio.h:4458
Definition miniaudio.h:5089
ma_format format
Definition miniaudio.h:5090
ma_uint32 channels
Definition miniaudio.h:5091
ma_uint32 sampleRate
Definition miniaudio.h:5092
Definition miniaudio.h:5098
float volumeEnd
Definition miniaudio.h:5101
ma_int64 cursorInFrames
Definition miniaudio.h:5103
ma_uint64 lengthInFrames
Definition miniaudio.h:5102
float volumeBeg
Definition miniaudio.h:5100
ma_fader_config config
Definition miniaudio.h:5099
Definition miniaudio.h:6247
ma_uint32 counter
Definition miniaudio.h:6251
ma_event e
Definition miniaudio.h:6249
Definition miniaudio.h:9873
ma_uint64 sizeInBytes
Definition miniaudio.h:9874
Definition miniaudio.h:5019
ma_uint32 channels
Definition miniaudio.h:5020
ma_uint32 smoothTimeInFrames
Definition miniaudio.h:5021
Definition miniaudio.h:5028
ma_gainer_config config
Definition miniaudio.h:5029
ma_uint32 t
Definition miniaudio.h:5030
float masterVolume
Definition miniaudio.h:5031
float * pNewGains
Definition miniaudio.h:5033
ma_bool32 _ownsHeap
Definition miniaudio.h:5037
float * pOldGains
Definition miniaudio.h:5032
void * _pHeap
Definition miniaudio.h:5036
Definition miniaudio.h:4955
double shelfSlope
Definition miniaudio.h:4960
double frequency
Definition miniaudio.h:4961
ma_uint32 sampleRate
Definition miniaudio.h:4958
ma_uint32 channels
Definition miniaudio.h:4957
double gainDB
Definition miniaudio.h:4959
ma_format format
Definition miniaudio.h:4956
Definition miniaudio.h:4967
ma_biquad bq
Definition miniaudio.h:4968
Definition miniaudio.h:11041
ma_node_config nodeConfig
Definition miniaudio.h:11042
ma_hishelf_config hishelf
Definition miniaudio.h:11043
Definition miniaudio.h:11050
ma_node_base baseNode
Definition miniaudio.h:11051
ma_hishelf2 hishelf
Definition miniaudio.h:11052
Definition miniaudio.h:4715
ma_uint32 channels
Definition miniaudio.h:4717
double q
Definition miniaudio.h:4720
ma_format format
Definition miniaudio.h:4716
double cutoffFrequency
Definition miniaudio.h:4719
ma_uint32 sampleRate
Definition miniaudio.h:4718
Definition miniaudio.h:4727
void * _pHeap
Definition miniaudio.h:4734
ma_uint32 channels
Definition miniaudio.h:4729
ma_bool32 _ownsHeap
Definition miniaudio.h:4735
ma_biquad_coefficient * pR1
Definition miniaudio.h:4731
ma_format format
Definition miniaudio.h:4728
ma_biquad_coefficient a
Definition miniaudio.h:4730
Definition miniaudio.h:4747
ma_biquad bq
Definition miniaudio.h:4748
Definition miniaudio.h:4761
ma_uint32 order
Definition miniaudio.h:4766
ma_format format
Definition miniaudio.h:4762
double cutoffFrequency
Definition miniaudio.h:4765
ma_uint32 channels
Definition miniaudio.h:4763
ma_uint32 sampleRate
Definition miniaudio.h:4764
Definition miniaudio.h:10926
ma_hpf_config hpf
Definition miniaudio.h:10928
ma_node_config nodeConfig
Definition miniaudio.h:10927
Definition miniaudio.h:10935
ma_hpf hpf
Definition miniaudio.h:10937
ma_node_base baseNode
Definition miniaudio.h:10936
Definition miniaudio.h:4772
ma_format format
Definition miniaudio.h:4773
ma_uint32 sampleRate
Definition miniaudio.h:4775
ma_uint32 hpf2Count
Definition miniaudio.h:4777
ma_bool32 _ownsHeap
Definition miniaudio.h:4783
ma_hpf2 * pHPF2
Definition miniaudio.h:4779
ma_uint32 hpf1Count
Definition miniaudio.h:4776
void * _pHeap
Definition miniaudio.h:4782
ma_uint32 channels
Definition miniaudio.h:4774
ma_hpf1 * pHPF1
Definition miniaudio.h:4778
Definition miniaudio.h:6533
ma_uint32 flags
Definition miniaudio.h:6534
ma_uint32 capacity
Definition miniaudio.h:6535
Definition miniaudio.h:6542
ma_uint32 flags
Definition miniaudio.h:6543
MA_ATOMIC(8, ma_uint64) head
void * _pHeap
Definition miniaudio.h:6557
MA_ATOMIC(8, ma_uint64) tail
ma_semaphore sem
Definition miniaudio.h:6548
ma_job * pJobs
Definition miniaudio.h:6551
ma_spinlock lock
Definition miniaudio.h:6553
ma_bool32 _ownsHeap
Definition miniaudio.h:6558
ma_uint32 capacity
Definition miniaudio.h:6544
ma_slot_allocator allocator
Definition miniaudio.h:6550
Definition miniaudio.h:6400
union ma_job::@076155030205377027121332272104201172224350310324::@266325277153322137345014245330342376064260002265::@130367164226134276326243150275375137345226143214 aaudio
struct ma_job::@076155030205377027121332272104201172224350310324::@162326072202062220341126002226165353316212003225::@043256355247053313261044377353007260225105275377 freeDataStream
struct ma_job::@076155030205377027121332272104201172224350310324::@162326072202062220341126002226165353316212003225::@313236305214160340041206326262136175157151233212 freeDataBufferNode
struct ma_job::@076155030205377027121332272104201172224350310324::@162326072202062220341126002226165353316212003225::@340046265203306354230370154311374277231246022171 seekDataStream
union ma_job::@076155030205377027121332272104201172224350310324::@266325277153322137345014245330342376064260002265 device
ma_uint16 code
Definition miniaudio.h:6405
ma_uint64 rangeEndInPCMFrames
Definition miniaudio.h:6463
char * pFilePath
Definition miniaudio.h:6431
ma_uint32 flags
Definition miniaudio.h:6433
ma_uint64 loopPointBegInPCMFrames
Definition miniaudio.h:6464
ma_async_notification * pDoneNotification
Definition miniaudio.h:6435
ma_uint32 refcount
Definition miniaudio.h:6407
struct ma_job::@076155030205377027121332272104201172224350310324::@162326072202062220341126002226165353316212003225::@010225072324131341030247076375207170353070336076 loadDataBufferNode
ma_async_notification * pInitNotification
Definition miniaudio.h:6434
struct ma_job::@076155030205377027121332272104201172224350310324::@162326072202062220341126002226165353316212003225::@252272345363063376141306261073031065066047230230 pageDataBufferNode
wchar_t * pFilePathW
Definition miniaudio.h:6432
struct ma_job::@076155030205377027121332272104201172224350310324::@162326072202062220341126002226165353316212003225::@212355304046366273061322313162061272252160031026 freeDataBuffer
struct ma_job::@076155030205377027121332272104201172224350310324::@162326072202062220341126002226165353316212003225::@055107066110045100333331063333363005040222246327 loadDataBuffer
ma_fence * pDoneFence
Definition miniaudio.h:6437
ma_uint32 pageIndex
Definition miniaudio.h:6493
void * pDataBufferNode
Definition miniaudio.h:6430
struct ma_job::@076155030205377027121332272104201172224350310324::@232162361131326322036224155166340340167030101024 custom
ma_uint64 frameIndex
Definition miniaudio.h:6498
ma_uintptr data1
Definition miniaudio.h:6421
ma_uint64 rangeBegInPCMFrames
Definition miniaudio.h:6462
ma_uint32 order
Definition miniaudio.h:6412
ma_uint64 loopPointEndInPCMFrames
Definition miniaudio.h:6465
void * pDataBuffer
Definition miniaudio.h:6457
struct ma_job::@076155030205377027121332272104201172224350310324::@162326072202062220341126002226165353316212003225::@370342223312121011057042316125012153064147263145 pageDataStream
ma_fence * pInitFence
Definition miniaudio.h:6436
union ma_job::@076155030205377027121332272104201172224350310324 data
ma_uint32 isLooping
Definition miniaudio.h:6466
void * pDevice
Definition miniaudio.h:6509
union ma_job::@372363176050363073375261041000274313066305012270 toc
union ma_job::@076155030205377027121332272104201172224350310324::@162326072202062220341126002226165353316212003225 resourceManager
ma_job_proc proc
Definition miniaudio.h:6419
void * pDecoder
Definition miniaudio.h:6450
MA_ATOMIC(8, ma_uint64) next
ma_uint64 initialSeekPoint
Definition miniaudio.h:6480
struct ma_job::@372363176050363073375261041000274313066305012270::@016145024237302151001053066056242034141361121320 breakup
struct ma_job::@076155030205377027121332272104201172224350310324::@266325277153322137345014245330342376064260002265::@130367164226134276326243150275375137345226143214::@257372157010040340170351215234263363027105000072 reroute
ma_uint64 allocation
Definition miniaudio.h:6409
ma_uint32 deviceType
Definition miniaudio.h:6510
ma_uint16 slot
Definition miniaudio.h:6406
ma_uintptr data0
Definition miniaudio.h:6420
struct ma_job::@076155030205377027121332272104201172224350310324::@162326072202062220341126002226165353316212003225::@022353077243363366313273203245322164334206307067 loadDataStream
void * pDataStream
Definition miniaudio.h:6477
void * pResourceManager
Definition miniaudio.h:6429
Definition miniaudio.h:4362
ma_int32 state
Definition miniaudio.h:4363
Definition miniaudio.h:5312
ma_format format
Definition miniaudio.h:5313
double lpfNyquistFactor
Definition miniaudio.h:5318
ma_uint32 lpfOrder
Definition miniaudio.h:5317
ma_uint32 sampleRateOut
Definition miniaudio.h:5316
ma_uint32 channels
Definition miniaudio.h:5314
ma_uint32 sampleRateIn
Definition miniaudio.h:5315
Definition miniaudio.h:5324
union ma_linear_resampler::@072230056061363164356013047006353302004131167327 x0
ma_linear_resampler_config config
Definition miniaudio.h:5325
union ma_linear_resampler::@352367145231272160211021055022354066054130141316 x1
ma_lpf lpf
Definition miniaudio.h:5340
ma_bool32 _ownsHeap
Definition miniaudio.h:5344
float * f32
Definition miniaudio.h:5332
ma_uint32 inAdvanceInt
Definition miniaudio.h:5326
ma_uint32 inTimeInt
Definition miniaudio.h:5328
ma_uint32 inAdvanceFrac
Definition miniaudio.h:5327
void * _pHeap
Definition miniaudio.h:5343
ma_uint32 inTimeFrac
Definition miniaudio.h:5329
ma_int16 * s16
Definition miniaudio.h:5333
Definition miniaudio.h:4541
ma_log_callback_proc onLog
Definition miniaudio.h:4542
void * pUserData
Definition miniaudio.h:4543
Definition miniaudio.h:4550
ma_log_callback callbacks[MA_MAX_LOG_CALLBACKS]
Definition miniaudio.h:4551
ma_allocation_callbacks allocationCallbacks
Definition miniaudio.h:4553
ma_uint32 callbackCount
Definition miniaudio.h:4552
ma_mutex lock
Definition miniaudio.h:4555
Definition miniaudio.h:4924
double frequency
Definition miniaudio.h:4930
ma_uint32 sampleRate
Definition miniaudio.h:4927
double gainDB
Definition miniaudio.h:4928
ma_format format
Definition miniaudio.h:4925
double shelfSlope
Definition miniaudio.h:4929
ma_uint32 channels
Definition miniaudio.h:4926
Definition miniaudio.h:4936
ma_biquad bq
Definition miniaudio.h:4937
Definition miniaudio.h:11018
ma_node_config nodeConfig
Definition miniaudio.h:11019
ma_loshelf_config loshelf
Definition miniaudio.h:11020
Definition miniaudio.h:11027
ma_loshelf2 loshelf
Definition miniaudio.h:11029
ma_node_base baseNode
Definition miniaudio.h:11028
Definition miniaudio.h:4626
ma_format format
Definition miniaudio.h:4627
ma_uint32 channels
Definition miniaudio.h:4628
double q
Definition miniaudio.h:4631
double cutoffFrequency
Definition miniaudio.h:4630
ma_uint32 sampleRate
Definition miniaudio.h:4629
Definition miniaudio.h:4638
ma_bool32 _ownsHeap
Definition miniaudio.h:4646
ma_biquad_coefficient * pR1
Definition miniaudio.h:4642
ma_format format
Definition miniaudio.h:4639
ma_biquad_coefficient a
Definition miniaudio.h:4641
void * _pHeap
Definition miniaudio.h:4645
ma_uint32 channels
Definition miniaudio.h:4640
Definition miniaudio.h:4659
ma_biquad bq
Definition miniaudio.h:4660
Definition miniaudio.h:4674
ma_uint32 channels
Definition miniaudio.h:4676
double cutoffFrequency
Definition miniaudio.h:4678
ma_uint32 order
Definition miniaudio.h:4679
ma_format format
Definition miniaudio.h:4675
ma_uint32 sampleRate
Definition miniaudio.h:4677
Definition miniaudio.h:10903
ma_lpf_config lpf
Definition miniaudio.h:10905
ma_node_config nodeConfig
Definition miniaudio.h:10904
Definition miniaudio.h:10912
ma_lpf lpf
Definition miniaudio.h:10914
ma_node_base baseNode
Definition miniaudio.h:10913
Definition miniaudio.h:4685
ma_format format
Definition miniaudio.h:4686
ma_lpf1 * pLPF1
Definition miniaudio.h:4691
ma_uint32 channels
Definition miniaudio.h:4687
ma_bool32 _ownsHeap
Definition miniaudio.h:4696
ma_lpf2 * pLPF2
Definition miniaudio.h:4692
void * _pHeap
Definition miniaudio.h:4695
ma_uint32 lpf2Count
Definition miniaudio.h:4690
ma_uint32 lpf1Count
Definition miniaudio.h:4689
ma_uint32 sampleRate
Definition miniaudio.h:4688
Definition miniaudio.h:10746
ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]
Definition miniaudio.h:10768
MA_ATOMIC(8, ma_uint64) localTime
MA_ATOMIC(8, ma_uint64) stateTimes[2]
ma_bool32 _ownsHeap
Definition miniaudio.h:10771
ma_node_graph * pNodeGraph
Definition miniaudio.h:10748
ma_uint16 cachedFrameCountIn
Definition miniaudio.h:10759
ma_uint16 consumedFrameCountIn
Definition miniaudio.h:10760
void * _pHeap
Definition miniaudio.h:10770
const ma_node_vtable * vtable
Definition miniaudio.h:10749
ma_uint32 outputBusCount
Definition miniaudio.h:10751
ma_uint32 inputBusCount
Definition miniaudio.h:10750
ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]
Definition miniaudio.h:10769
ma_uint16 cachedDataCapInFramesPerBus
Definition miniaudio.h:10755
ma_uint16 cachedFrameCountOut
Definition miniaudio.h:10758
float * pCachedData
Definition miniaudio.h:10754
ma_node_input_bus * pInputBuses
Definition miniaudio.h:10752
MA_ATOMIC(4, ma_node_state) state
ma_node_output_bus * pOutputBuses
Definition miniaudio.h:10753
Definition miniaudio.h:10691
ma_uint32 outputBusCount
Definition miniaudio.h:10695
ma_uint32 inputBusCount
Definition miniaudio.h:10694
const ma_node_vtable * vtable
Definition miniaudio.h:10692
const ma_uint32 * pInputChannels
Definition miniaudio.h:10696
const ma_uint32 * pOutputChannels
Definition miniaudio.h:10697
ma_node_state initialState
Definition miniaudio.h:10693
Definition miniaudio.h:10799
ma_uint32 processingSizeInFrames
Definition miniaudio.h:10801
ma_uint32 channels
Definition miniaudio.h:10800
size_t preMixStackSizeInBytes
Definition miniaudio.h:10802
Definition miniaudio.h:10809
ma_uint32 processingCacheFramesRemaining
Definition miniaudio.h:10814
ma_uint32 processingSizeInFrames
Definition miniaudio.h:10815
ma_node_base base
Definition miniaudio.h:10811
MA_ATOMIC(4, ma_bool32) isReading
ma_stack * pPreMixStack
Definition miniaudio.h:10821
float * pProcessingCache
Definition miniaudio.h:10813
ma_node_base endpoint
Definition miniaudio.h:10812
Definition miniaudio.h:10733
ma_node_output_bus head
Definition miniaudio.h:10735
ma_uint8 channels
Definition miniaudio.h:10740
MA_ATOMIC(4, ma_spinlock) lock
MA_ATOMIC(4, ma_uint32) nextCounter
Definition miniaudio.h:10709
ma_uint8 channels
Definition miniaudio.h:10713
MA_ATOMIC(4, ma_uint32) refCount
MA_ATOMIC(4, ma_bool32) isAttached
ma_node * pNode
Definition miniaudio.h:10711
MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus *) pNext
MA_ATOMIC(4, ma_uint32) flags
MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus *) pPrev
ma_uint8 outputBusIndex
Definition miniaudio.h:10712
MA_ATOMIC(4, ma_spinlock) lock
ma_uint8 inputNodeInputBusIndex
Definition miniaudio.h:10716
MA_ATOMIC(MA_SIZEOF_PTR, ma_node *) pInputNode
MA_ATOMIC(4, float) volume
Definition miniaudio.h:10648
ma_uint8 outputBusCount
Definition miniaudio.h:10681
ma_uint32 flags
Definition miniaudio.h:10687
void(* onProcess)(ma_node *pNode, const float **ppFramesIn, ma_uint32 *pFrameCountIn, float **ppFramesOut, ma_uint32 *pFrameCountOut)
Definition miniaudio.h:10660
ma_result(* onGetRequiredInputFrameCount)(ma_node *pNode, ma_uint32 outputFrameCount, ma_uint32 *pInputFrameCount)
Definition miniaudio.h:10669
ma_uint8 inputBusCount
Definition miniaudio.h:10675
Definition miniaudio.h:10230
double amplitude
Definition miniaudio.h:10235
ma_noise_type type
Definition miniaudio.h:10233
ma_format format
Definition miniaudio.h:10231
ma_bool32 duplicateChannels
Definition miniaudio.h:10236
ma_int32 seed
Definition miniaudio.h:10234
ma_uint32 channels
Definition miniaudio.h:10232
Definition miniaudio.h:10242
void * _pHeap
Definition miniaudio.h:10261
struct ma_noise::@272320325161151057051071113127331126240173110106::@263010177134363057254305244227100377212060056262 pink
union ma_noise::@272320325161151057051071113127331126240173110106 state
ma_uint32 * counter
Definition miniaudio.h:10252
ma_bool32 _ownsHeap
Definition miniaudio.h:10262
double * accumulation
Definition miniaudio.h:10251
ma_noise_config config
Definition miniaudio.h:10244
struct ma_noise::@272320325161151057051071113127331126240173110106::@136006061067250103002050101132350224037012373231 brownian
ma_data_source_base ds
Definition miniaudio.h:10243
ma_lcg lcg
Definition miniaudio.h:10245
double ** bin
Definition miniaudio.h:10250
Definition miniaudio.h:4863
ma_uint32 sampleRate
Definition miniaudio.h:4866
double q
Definition miniaudio.h:4867
double frequency
Definition miniaudio.h:4868
ma_uint32 channels
Definition miniaudio.h:4865
ma_format format
Definition miniaudio.h:4864
Definition miniaudio.h:4874
ma_biquad bq
Definition miniaudio.h:4875
Definition miniaudio.h:10972
ma_notch_config notch
Definition miniaudio.h:10974
ma_node_config nodeConfig
Definition miniaudio.h:10973
Definition miniaudio.h:10981
ma_node_base baseNode
Definition miniaudio.h:10982
ma_notch2 notch
Definition miniaudio.h:10983
Definition miniaudio.h:5976
ma_paged_audio_buffer_data * pData
Definition miniaudio.h:5977
Definition miniaudio.h:5957
MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page *) pTail
ma_format format
Definition miniaudio.h:5958
ma_paged_audio_buffer_page head
Definition miniaudio.h:5960
ma_uint32 channels
Definition miniaudio.h:5959
Definition miniaudio.h:5950
ma_uint8 pAudioData[1]
Definition miniaudio.h:5953
ma_uint64 sizeInFrames
Definition miniaudio.h:5952
MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page *) pNext
Definition miniaudio.h:5984
ma_uint64 relativeCursor
Definition miniaudio.h:5988
ma_data_source_base ds
Definition miniaudio.h:5985
ma_paged_audio_buffer_data * pData
Definition miniaudio.h:5986
ma_paged_audio_buffer_page * pCurrent
Definition miniaudio.h:5987
ma_uint64 absoluteCursor
Definition miniaudio.h:5989
Definition miniaudio.h:5060
ma_format format
Definition miniaudio.h:5061
float pan
Definition miniaudio.h:5064
ma_uint32 channels
Definition miniaudio.h:5062
ma_pan_mode mode
Definition miniaudio.h:5063
Definition miniaudio.h:5071
ma_pan_mode mode
Definition miniaudio.h:5074
ma_uint32 channels
Definition miniaudio.h:5073
float pan
Definition miniaudio.h:5075
ma_format format
Definition miniaudio.h:5072
Definition miniaudio.h:6039
ma_uint32 channels
Definition miniaudio.h:6043
ma_data_source_base ds
Definition miniaudio.h:6040
ma_rb rb
Definition miniaudio.h:6041
ma_format format
Definition miniaudio.h:6042
ma_uint32 sampleRate
Definition miniaudio.h:6044
Definition miniaudio.h:4893
double q
Definition miniaudio.h:4898
double gainDB
Definition miniaudio.h:4897
double frequency
Definition miniaudio.h:4899
ma_uint32 sampleRate
Definition miniaudio.h:4896
ma_format format
Definition miniaudio.h:4894
ma_uint32 channels
Definition miniaudio.h:4895
Definition miniaudio.h:4905
ma_biquad bq
Definition miniaudio.h:4906
Definition miniaudio.h:10995
ma_node_config nodeConfig
Definition miniaudio.h:10996
ma_peak_config peak
Definition miniaudio.h:10997
Definition miniaudio.h:11004
ma_peak2 peak
Definition miniaudio.h:11006
ma_node_base baseNode
Definition miniaudio.h:11005
Definition miniaudio.h:10195
double amplitude
Definition miniaudio.h:10200
double dutyCycle
Definition miniaudio.h:10199
ma_format format
Definition miniaudio.h:10196
double frequency
Definition miniaudio.h:10201
ma_uint32 sampleRate
Definition miniaudio.h:10198
ma_uint32 channels
Definition miniaudio.h:10197
Definition miniaudio.h:10207
ma_pulsewave_config config
Definition miniaudio.h:10209
ma_waveform waveform
Definition miniaudio.h:10208
Definition miniaudio.h:6007
void * pBuffer
Definition miniaudio.h:6008
ma_allocation_callbacks allocationCallbacks
Definition miniaudio.h:6016
ma_bool8 clearOnWriteAcquire
Definition miniaudio.h:6015
MA_ATOMIC(4, ma_uint32) encodedReadOffset
ma_uint32 subbufferCount
Definition miniaudio.h:6010
ma_uint32 subbufferSizeInBytes
Definition miniaudio.h:6009
MA_ATOMIC(4, ma_uint32) encodedWriteOffset
ma_uint32 subbufferStrideInBytes
Definition miniaudio.h:6011
ma_bool8 ownsBuffer
Definition miniaudio.h:6014
Definition miniaudio.h:5385
struct ma_resampler_config::@034062305035031270273003361176343331032024023276 linear
ma_resample_algorithm algorithm
Definition miniaudio.h:5390
ma_uint32 channels
Definition miniaudio.h:5387
ma_resampling_backend_vtable * pBackendVTable
Definition miniaudio.h:5391
ma_uint32 sampleRateIn
Definition miniaudio.h:5388
ma_uint32 sampleRateOut
Definition miniaudio.h:5389
ma_uint32 lpfOrder
Definition miniaudio.h:5395
ma_format format
Definition miniaudio.h:5386
void * pBackendUserData
Definition miniaudio.h:5392
Definition miniaudio.h:5402
void * _pHeap
Definition miniaudio.h:5416
union ma_resampler::@337055015201247271010137327016176055075177120235 state
ma_resampling_backend_vtable * pBackendVTable
Definition miniaudio.h:5404
ma_bool32 _ownsHeap
Definition miniaudio.h:5417
ma_resampling_backend * pBackend
Definition miniaudio.h:5403
void * pBackendUserData
Definition miniaudio.h:5405
ma_uint32 channels
Definition miniaudio.h:5407
ma_uint32 sampleRateIn
Definition miniaudio.h:5408
ma_uint32 sampleRateOut
Definition miniaudio.h:5409
ma_linear_resampler linear
Definition miniaudio.h:5412
ma_format format
Definition miniaudio.h:5406
Definition miniaudio.h:5365
ma_uint64(* onGetOutputLatency)(void *pUserData, const ma_resampling_backend *pBackend)
Definition miniaudio.h:5372
ma_result(* onGetRequiredInputFrameCount)(void *pUserData, const ma_resampling_backend *pBackend, ma_uint64 outputFrameCount, ma_uint64 *pInputFrameCount)
Definition miniaudio.h:5373
ma_uint64(* onGetInputLatency)(void *pUserData, const ma_resampling_backend *pBackend)
Definition miniaudio.h:5371
ma_result(* onProcess)(void *pUserData, ma_resampling_backend *pBackend, const void *pFramesIn, ma_uint64 *pFrameCountIn, void *pFramesOut, ma_uint64 *pFrameCountOut)
Definition miniaudio.h:5369
ma_result(* onGetExpectedOutputFrameCount)(void *pUserData, const ma_resampling_backend *pBackend, ma_uint64 inputFrameCount, ma_uint64 *pOutputFrameCount)
Definition miniaudio.h:5374
ma_result(* onSetRate)(void *pUserData, ma_resampling_backend *pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
Definition miniaudio.h:5370
void(* onUninit)(void *pUserData, ma_resampling_backend *pBackend, const ma_allocation_callbacks *pAllocationCallbacks)
Definition miniaudio.h:5368
ma_result(* onReset)(void *pUserData, ma_resampling_backend *pBackend)
Definition miniaudio.h:5375
ma_result(* onInit)(void *pUserData, const ma_resampler_config *pConfig, void *pHeap, ma_resampling_backend **ppBackend)
Definition miniaudio.h:5367
ma_result(* onGetHeapSize)(void *pUserData, const ma_resampler_config *pConfig, size_t *pHeapSizeInBytes)
Definition miniaudio.h:5366
Definition miniaudio.h:10490
ma_vfs * pVFS
Definition miniaudio.h:10500
ma_log * pLog
Definition miniaudio.h:10492
ma_format decodedFormat
Definition miniaudio.h:10493
ma_uint32 jobThreadCount
Definition miniaudio.h:10496
size_t jobThreadStackSize
Definition miniaudio.h:10497
void * pCustomDecodingBackendUserData
Definition miniaudio.h:10503
ma_uint32 decodedSampleRate
Definition miniaudio.h:10495
ma_uint32 flags
Definition miniaudio.h:10499
ma_uint32 decodedChannels
Definition miniaudio.h:10494
ma_allocation_callbacks allocationCallbacks
Definition miniaudio.h:10491
ma_uint32 jobQueueCapacity
Definition miniaudio.h:10498
ma_decoding_backend_vtable ** ppCustomDecodingBackendVTables
Definition miniaudio.h:10501
ma_uint32 customDecodingBackendCount
Definition miniaudio.h:10502
Definition miniaudio.h:10414
ma_bool32 isDataOwnedByResourceManager
Definition miniaudio.h:10420
ma_resource_manager_data_buffer_node * pChildHi
Definition miniaudio.h:10424
ma_uint32 hashedName32
Definition miniaudio.h:10415
ma_resource_manager_data_buffer_node * pChildLo
Definition miniaudio.h:10423
ma_uint32 refCount
Definition miniaudio.h:10416
MA_ATOMIC(4, ma_uint32) executionPointer
MA_ATOMIC(4, ma_uint32) executionCounter
ma_resource_manager_data_supply data
Definition miniaudio.h:10421
ma_resource_manager_data_buffer_node * pParent
Definition miniaudio.h:10422
Definition miniaudio.h:10428
ma_resource_manager_data_buffer_node * pNode
Definition miniaudio.h:10431
union ma_resource_manager_data_buffer::@147102050076145223307363010000254034045111003316 connector
MA_ATOMIC(4, ma_uint32) executionCounter
ma_uint64 seekTargetInPCMFrames
Definition miniaudio.h:10435
MA_ATOMIC(4, ma_result) result
ma_atomic_bool32 isConnectorInitialized
Definition miniaudio.h:10439
ma_resource_manager * pResourceManager
Definition miniaudio.h:10430
MA_ATOMIC(4, ma_bool32) isLooping
ma_paged_audio_buffer pagedBuffer
Definition miniaudio.h:10444
ma_data_source_base ds
Definition miniaudio.h:10429
ma_uint32 flags
Definition miniaudio.h:10432
ma_bool32 seekToCursorOnNextRead
Definition miniaudio.h:10436
ma_audio_buffer buffer
Definition miniaudio.h:10443
ma_decoder decoder
Definition miniaudio.h:10442
MA_ATOMIC(4, ma_uint32) executionPointer
Definition miniaudio.h:10361
const ma_resource_manager_pipeline_notifications * pNotifications
Definition miniaudio.h:10364
ma_uint64 rangeBegInPCMFrames
Definition miniaudio.h:10366
ma_uint64 rangeEndInPCMFrames
Definition miniaudio.h:10367
ma_bool32 isLooping
Definition miniaudio.h:10371
const char * pFilePath
Definition miniaudio.h:10362
ma_uint32 flags
Definition miniaudio.h:10370
const wchar_t * pFilePathW
Definition miniaudio.h:10363
ma_uint64 loopPointEndInPCMFrames
Definition miniaudio.h:10369
ma_uint64 initialSeekPointInPCMFrames
Definition miniaudio.h:10365
ma_uint64 loopPointBegInPCMFrames
Definition miniaudio.h:10368
Definition miniaudio.h:10477
ma_uint32 flags
Definition miniaudio.h:10484
union ma_resource_manager_data_source::@004364174211163312054326036043074336257300260156 backend
ma_resource_manager_data_stream stream
Definition miniaudio.h:10481
MA_ATOMIC(4, ma_uint32) executionCounter
ma_resource_manager_data_buffer buffer
Definition miniaudio.h:10480
MA_ATOMIC(4, ma_uint32) executionPointer
Definition miniaudio.h:10449
ma_bool32 isDecoderInitialized
Definition miniaudio.h:10454
MA_ATOMIC(4, ma_uint32) executionPointer
MA_ATOMIC(4, ma_bool32) isPageValid[2]
MA_ATOMIC(4, ma_uint32) pageFrameCount[2]
ma_resource_manager * pResourceManager
Definition miniaudio.h:10451
ma_uint32 relativeCursor
Definition miniaudio.h:10456
ma_uint64 totalLengthInPCMFrames
Definition miniaudio.h:10455
MA_ATOMIC(4, ma_bool32) seekCounter
ma_uint32 currentPageIndex
Definition miniaudio.h:10458
ma_decoder decoder
Definition miniaudio.h:10453
void * pPageData
Definition miniaudio.h:10466
ma_data_source_base ds
Definition miniaudio.h:10450
MA_ATOMIC(8, ma_uint64) absoluteCursor
MA_ATOMIC(4, ma_result) result
MA_ATOMIC(4, ma_bool32) isDecoderAtEnd
ma_uint32 flags
Definition miniaudio.h:10452
MA_ATOMIC(4, ma_bool32) isLooping
MA_ATOMIC(4, ma_uint32) executionCounter
Definition miniaudio.h:10386
struct ma_resource_manager_data_supply::@001233244156055325241162360210350111255332045115::@266031203173106021257260016135333327345224335152 decodedPaged
ma_uint64 decodedFrameCount
Definition miniaudio.h:10399
ma_uint64 totalFrameCount
Definition miniaudio.h:10398
ma_paged_audio_buffer_data data
Definition miniaudio.h:10406
ma_uint32 sampleRate
Definition miniaudio.h:10402
size_t sizeInBytes
Definition miniaudio.h:10393
union ma_resource_manager_data_supply::@001233244156055325241162360210350111255332045115 backend
const void * pData
Definition miniaudio.h:10392
MA_ATOMIC(4, ma_resource_manager_data_supply_type) type
struct ma_resource_manager_data_supply::@001233244156055325241162360210350111255332045115::@053004310174343203110056014105256005244236372256 encoded
ma_uint32 channels
Definition miniaudio.h:10401
ma_format format
Definition miniaudio.h:10400
struct ma_resource_manager_data_supply::@001233244156055325241162360210350111255332045115::@036244371007162251135250200112160307314010244127 decoded
Definition miniaudio.h:10316
ma_resource_manager_pipeline_stage_notification done
Definition miniaudio.h:10318
ma_resource_manager_pipeline_stage_notification init
Definition miniaudio.h:10317
ma_fence * pFence
Definition miniaudio.h:10312
ma_async_notification * pNotification
Definition miniaudio.h:10311
Definition miniaudio.h:10509
ma_resource_manager_config config
Definition miniaudio.h:10510
ma_log log
Definition miniaudio.h:10518
ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]
Definition miniaudio.h:10514
ma_mutex dataBufferBSTLock
Definition miniaudio.h:10513
ma_job_queue jobQueue
Definition miniaudio.h:10516
ma_default_vfs defaultVFS
Definition miniaudio.h:10517
ma_resource_manager_data_buffer_node * pRootDataBufferNode
Definition miniaudio.h:10511
Definition miniaudio.h:4466
ma_pthread_mutex_t lock
Definition miniaudio.h:4468
int value
Definition miniaudio.h:4467
ma_pthread_cond_t cond
Definition miniaudio.h:4469
Definition miniaudio.h:6334
ma_uint32 capacity
Definition miniaudio.h:6335
Definition miniaudio.h:6342
MA_ATOMIC(4, ma_uint32) bitfield
Definition miniaudio.h:6347
ma_slot_allocator_group * pGroups
Definition miniaudio.h:6348
ma_uint32 * pSlots
Definition miniaudio.h:6349
ma_uint32 count
Definition miniaudio.h:6350
ma_bool32 _ownsHeap
Definition miniaudio.h:6354
void * _pHeap
Definition miniaudio.h:6355
ma_uint32 capacity
Definition miniaudio.h:6351
Definition miniaudio.h:11190
ma_uint32 flags
Definition miniaudio.h:11199
ma_data_source * pDataSource
Definition miniaudio.h:11193
ma_sound_end_proc endCallback
Definition miniaudio.h:11206
ma_resource_manager_pipeline_notifications initNotifications
Definition miniaudio.h:11209
ma_uint64 rangeBegInPCMFrames
Definition miniaudio.h:11202
ma_uint64 initialSeekPointInPCMFrames
Definition miniaudio.h:11201
ma_uint64 rangeEndInPCMFrames
Definition miniaudio.h:11203
ma_uint32 channelsIn
Definition miniaudio.h:11196
ma_node * pInitialAttachment
Definition miniaudio.h:11194
ma_uint32 volumeSmoothTimeInPCMFrames
Definition miniaudio.h:11200
ma_fence * pDoneFence
Definition miniaudio.h:11211
ma_mono_expansion_mode monoExpansionMode
Definition miniaudio.h:11198
ma_uint64 loopPointBegInPCMFrames
Definition miniaudio.h:11204
ma_uint64 loopPointEndInPCMFrames
Definition miniaudio.h:11205
ma_uint32 initialAttachmentInputBusIndex
Definition miniaudio.h:11195
ma_bool32 isLooping
Definition miniaudio.h:11212
ma_uint32 channelsOut
Definition miniaudio.h:11197
const wchar_t * pFilePathW
Definition miniaudio.h:11192
void * pEndCallbackUserData
Definition miniaudio.h:11207
const char * pFilePath
Definition miniaudio.h:11191
Definition miniaudio.h:11240
ma_sound sound
Definition miniaudio.h:11241
ma_sound_inlined * pPrev
Definition miniaudio.h:11243
ma_sound_inlined * pNext
Definition miniaudio.h:11242
Definition miniaudio.h:11219
ma_bool8 ownsDataSource
Definition miniaudio.h:11226
ma_engine_node engineNode
Definition miniaudio.h:11220
ma_data_source * pDataSource
Definition miniaudio.h:11221
ma_resource_manager_data_source * pResourceManagerDataSource
Definition miniaudio.h:11233
MA_ATOMIC(4, ma_bool32) atEnd
void * pEndCallbackUserData
Definition miniaudio.h:11225
ma_sound_end_proc endCallback
Definition miniaudio.h:11224
MA_ATOMIC(8, ma_uint64) seekTarget
Definition miniaudio.h:5200
ma_attenuation_model attenuationModel
Definition miniaudio.h:5204
float maxDistance
Definition miniaudio.h:5210
float dopplerFactor
Definition miniaudio.h:5215
ma_uint32 gainSmoothTimeInFrames
Definition miniaudio.h:5218
float directionalAttenuationFactor
Definition miniaudio.h:5216
float coneOuterAngleInRadians
Definition miniaudio.h:5213
float minSpatializationChannelGain
Definition miniaudio.h:5217
float maxGain
Definition miniaudio.h:5208
ma_uint32 channelsIn
Definition miniaudio.h:5201
float minDistance
Definition miniaudio.h:5209
float coneOuterGain
Definition miniaudio.h:5214
ma_uint32 channelsOut
Definition miniaudio.h:5202
float minGain
Definition miniaudio.h:5207
float coneInnerAngleInRadians
Definition miniaudio.h:5212
ma_handedness handedness
Definition miniaudio.h:5206
ma_positioning positioning
Definition miniaudio.h:5205
float rolloff
Definition miniaudio.h:5211
ma_channel * pChannelMapIn
Definition miniaudio.h:5203
Definition miniaudio.h:5151
ma_channel * pChannelMapOut
Definition miniaudio.h:5153
ma_handedness handedness
Definition miniaudio.h:5154
float speedOfSound
Definition miniaudio.h:5158
float coneOuterGain
Definition miniaudio.h:5157
ma_vec3f worldUp
Definition miniaudio.h:5159
ma_uint32 channelsOut
Definition miniaudio.h:5152
float coneOuterAngleInRadians
Definition miniaudio.h:5156
float coneInnerAngleInRadians
Definition miniaudio.h:5155
Definition miniaudio.h:5166
ma_spatializer_listener_config config
Definition miniaudio.h:5167
ma_atomic_vec3f position
Definition miniaudio.h:5168
ma_atomic_vec3f velocity
Definition miniaudio.h:5170
ma_atomic_vec3f direction
Definition miniaudio.h:5169
ma_bool32 isEnabled
Definition miniaudio.h:5171
void * _pHeap
Definition miniaudio.h:5175
ma_bool32 _ownsHeap
Definition miniaudio.h:5174
Definition miniaudio.h:5225
ma_handedness handedness
Definition miniaudio.h:5231
ma_channel * pChannelMapIn
Definition miniaudio.h:5228
ma_uint32 channelsIn
Definition miniaudio.h:5226
float minDistance
Definition miniaudio.h:5234
ma_atomic_vec3f position
Definition miniaudio.h:5243
float maxGain
Definition miniaudio.h:5233
float coneOuterAngleInRadians
Definition miniaudio.h:5238
float maxDistance
Definition miniaudio.h:5235
float coneInnerAngleInRadians
Definition miniaudio.h:5237
float dopplerFactor
Definition miniaudio.h:5240
float * pNewChannelGainsOut
Definition miniaudio.h:5249
ma_uint32 gainSmoothTimeInFrames
Definition miniaudio.h:5242
void * _pHeap
Definition miniaudio.h:5252
ma_atomic_vec3f velocity
Definition miniaudio.h:5245
float rolloff
Definition miniaudio.h:5236
float directionalAttenuationFactor
Definition miniaudio.h:5241
float minGain
Definition miniaudio.h:5232
float dopplerPitch
Definition miniaudio.h:5246
ma_atomic_vec3f direction
Definition miniaudio.h:5244
ma_gainer gainer
Definition miniaudio.h:5248
ma_uint32 channelsOut
Definition miniaudio.h:5227
ma_positioning positioning
Definition miniaudio.h:5230
ma_bool32 _ownsHeap
Definition miniaudio.h:5253
float coneOuterGain
Definition miniaudio.h:5239
ma_attenuation_model attenuationModel
Definition miniaudio.h:5229
float minSpatializationChannelGain
Definition miniaudio.h:5247
Definition miniaudio.h:10858
ma_node_config nodeConfig
Definition miniaudio.h:10859
ma_uint32 channels
Definition miniaudio.h:10860
ma_uint32 outputBusCount
Definition miniaudio.h:10861
Definition miniaudio.h:10868
ma_node_base base
Definition miniaudio.h:10869
Definition miniaudio.h:10617
size_t sizeInBytes
Definition miniaudio.h:10619
size_t offset
Definition miniaudio.h:10618
unsigned char _data[1]
Definition miniaudio.h:10620
Definition miniaudio.h:5117
float z
Definition miniaudio.h:5120
float x
Definition miniaudio.h:5118
float y
Definition miniaudio.h:5119
Definition miniaudio.h:9878
ma_result(* onWrite)(ma_vfs *pVFS, ma_vfs_file file, const void *pSrc, size_t sizeInBytes, size_t *pBytesWritten)
Definition miniaudio.h:9883
ma_result(* onInfo)(ma_vfs *pVFS, ma_vfs_file file, ma_file_info *pInfo)
Definition miniaudio.h:9886
ma_result(* onOpen)(ma_vfs *pVFS, const char *pFilePath, ma_uint32 openMode, ma_vfs_file *pFile)
Definition miniaudio.h:9879
ma_result(* onTell)(ma_vfs *pVFS, ma_vfs_file file, ma_int64 *pCursor)
Definition miniaudio.h:9885
ma_result(* onSeek)(ma_vfs *pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
Definition miniaudio.h:9884
ma_result(* onRead)(ma_vfs *pVFS, ma_vfs_file file, void *pDst, size_t sizeInBytes, size_t *pBytesRead)
Definition miniaudio.h:9882
ma_result(* onOpenW)(ma_vfs *pVFS, const wchar_t *pFilePath, ma_uint32 openMode, ma_vfs_file *pFile)
Definition miniaudio.h:9880
ma_result(* onClose)(ma_vfs *pVFS, ma_vfs_file file)
Definition miniaudio.h:9881
Definition miniaudio.h:10166
ma_format format
Definition miniaudio.h:10167
ma_uint32 channels
Definition miniaudio.h:10168
double amplitude
Definition miniaudio.h:10171
ma_uint32 sampleRate
Definition miniaudio.h:10169
ma_waveform_type type
Definition miniaudio.h:10170
double frequency
Definition miniaudio.h:10172
Definition miniaudio.h:10178
ma_data_source_base ds
Definition miniaudio.h:10179
double advance
Definition miniaudio.h:10181
double time
Definition miniaudio.h:10182
ma_waveform_config config
Definition miniaudio.h:10180
Definition miniaudio.h:4574
float f32
Definition miniaudio.h:4575
ma_int32 s32
Definition miniaudio.h:4576
Definition miniaudio.h:7030
char coreaudio[256]
Definition miniaudio.h:7037
char webaudio[32]
Definition miniaudio.h:7043
ma_int32 aaudio
Definition miniaudio.h:7041
char pulse[256]
Definition miniaudio.h:7035
ma_uint32 opensl
Definition miniaudio.h:7042
void * p
Definition miniaudio.h:7048
char oss[64]
Definition miniaudio.h:7040
ma_uint32 winmm
Definition miniaudio.h:7033
ma_uint8 dsound[16]
Definition miniaudio.h:7032
int nullbackend
Definition miniaudio.h:7050
char audio4[256]
Definition miniaudio.h:7039
char alsa[256]
Definition miniaudio.h:7034
int jack
Definition miniaudio.h:7036
char sndio[256]
Definition miniaudio.h:7038
ma_wchar_win32 wasapi[64]
Definition miniaudio.h:7031
int i
Definition miniaudio.h:7046
char s[256]
Definition miniaudio.h:7047
Definition miniaudio.h:7024
double counterD
Definition miniaudio.h:7026
ma_int64 counter
Definition miniaudio.h:7025